Search for a command to run...
Progressive hints first, then the full explanation and implementation when you're ready to cash out.
Review status
AI-generated and still unreviewed. Double-check the details before internalizing them.
Hints
Open only as much as you need to keep the solve alive.
List the possible adjacent contributions. The important split is not four separate values; it is parity first.
Color {0,2} red and {1,3} blue. A red-blue boundary always contributes 1. Inside one color, equal values contribute 0, and switching between the two values contributes 2.
Any final permutation is an alternating sequence of red blocks and blue blocks. Once you know the numbers of red and blue blocks, the total red-blue boundary contribution is fixed.
For one color group, count ordered nonempty blocks by taking one binary string of its two symbols and cutting it into pieces. Runs tell you how many switch adjacencies exist; cuts decide which of those switches stop being internal.
For a binary string with L runs, there are D=L-1 switch adjacencies and S=len-L equal adjacencies. Choosing p switch cuts and q equal cuts creates p+q+1 blocks and leaves D-p internal switches. Build this table for red and blue, then convolve with the fixed boundary cost.
The first thing to do is throw away the fake complexity. There are only four values, and lowbit makes the adjacency cost very rigid:
00 with 2 or 1 with 3: contributes 21So split the values into two parity groups:
{0,2}{1,3}Every red-blue adjacent pair contributes exactly 1. Inside red, only a 0/2 switch contributes, and it contributes 2. Inside blue, only a 1/3 switch contributes, also 2.
That is the whole problem. The rest is just counting without faceplanting into overcounting.
Blocks
Look at a final permutation and merge consecutive red values into red blocks, and consecutive blue values into blue blocks. The colors of blocks must alternate.
If there are r red blocks and b blue blocks:
r=b: there are two possible color skeletons, starting with red or starting with blue. The number of color boundaries is 2r-1.r=b+1: the skeleton must start and end with red. The number of boundaries is 2b.b=r+1: the skeleton must start and end with blue. The number of boundaries is 2r.Those boundaries already give that many points of oddness. The internal parts of the blocks are independent between red and blue.
Define R[s][u] as the number of ways to split all c0 zeros and c2 twos into s ordered nonempty red blocks such that, inside those red blocks, there are exactly u adjacent switches between 0 and 2.
Each such switch contributes 2, so the red internal contribution is 2u. Define B[s][u] the same way for 1 and 3.
Then combining is direct:
s, add R[s][u] * B[s][v] * 2 to oddness 2(u+v) + (2s-1)R[s][u] * B[s+1][v] to oddness 2(u+v) + 2sR[s+1][u] * B[s][v] to oddness 2(u+v) + 2sThe factor 2 in the equal case is just the two possible starting colors. No magic, just alternating blocks.
Computing one table
Now solve the generic subproblem:
Given x copies of one symbol and y copies of another symbol, count ways to split them into ordered nonempty blocks, grouped by number of blocks and number of internal switches.
Take the ordered blocks and concatenate them. You get one binary string with x copies of the first symbol and y copies of the second. The block split is then exactly a choice of cut positions between adjacent characters of this string. This is a bijection, so it counts every possibility once.
Suppose the binary string has L runs. Example: 000222002 has 4 runs. Then:
D = L-1S = x+y-LFirst count how many binary strings have L runs.
If L is even, both symbols have L/2 runs, and there are two choices for the starting symbol. So the count is
2 * C(x-1, L/2-1) * C(y-1, L/2-1).
If L is odd, one symbol has (L+1)/2 runs and the other has L/2 runs. Either symbol can be the one with more runs, so we add both cases:
C(x-1, (L+1)/2-1) * C(y-1, L/2-1)
and
C(x-1, L/2-1) * C(y-1, (L+1)/2-1).
Why these binomials? Splitting x identical items into a positive run lengths is C(x-1,a-1). Same for y. Standard bars-and-stars, nothing cursed.
Now choose cuts.
Among the D switch adjacencies, choose p to cut. Among the S equal adjacencies, choose q to cut. Then:
p+q+1D-pSo for each valid run-count case, we add
ways_for_strings * C(D,p) * C(S,q)
to
table[p+q+1][D-p].
That builds the table in O(len^3) for one parity group.
Final algorithm
1e9+7 up to 800.(c0,c2).(c1,c3).The total complexity is O(n^3) per large test shape, and the sum of n is at most 800, so this is fine. A 2900 problem, yes, but the core trick is clean: parity blocks first, run cuts second.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
const int MOD = 1000000007;
const int MAXN = 800;
int C[MAXN + 1][MAXN + 1];
void addMod(int &a, int b) {
a += b;
if (a >= MOD) a -= MOD;
}
int mulMod(int a, int b) {
return int(1LL * a * b % MOD);
}
void precomputeCombinations() {
for (int i = 0; i <= MAXN; i++) {
C[i][0] = C[i][i] = 1;
for (int j = 1; j < i; j++) {
C[i][j] = C[i - 1][j];
addMod(C[i][j], C[i - 1][j - 1]);
}
}
}
vector<vector<int>> buildTable(int x, int y) {
int len = x + y;
vector<vector<int>> table(len + 2, vector<int>(len + 1));
auto addRuns = [&](int rx, int ry, int mult, int runs) {
if (rx <= 0 || ry <= 0 || rx > x || ry > y) return;
int base = mulMod(C[x - 1][rx - 1], C[y - 1][ry - 1]);
base = mulMod(base, mult);
int different = runs - 1;
int same = len - runs;
for (int cutDiff = 0; cutDiff <= different; cutDiff++) {
int waysDiff = mulMod(base, C[different][cutDiff]);
int keptSwitches = different - cutDiff;
for (int cutSame = 0; cutSame <= same; cutSame++) {
int segments = cutDiff + cutSame + 1;
int ways = mulMod(waysDiff, C[same][cutSame]);
addMod(table[segments][keptSwitches], ways);
}
}
};
for (int runs = 2; runs <= len; runs++) {
if (runs % 2 == 0) {
addRuns(runs / 2, runs / 2, 2, runs);
} else {
addRuns((runs + 1) / 2, runs / 2, 1, runs);
addRuns(runs / 2, (runs + 1) / 2, 1, runs);
}
}
return table;
}
int main() {
setIO();
precomputeCombinations();
int T;
cin >> T;
while (T--) {
int c0, c1, c2, c3;
cin >> c0 >> c1 >> c2 >> c3;
int n = c0 + c1 + c2 + c3;
int redLen = c0 + c2;
int blueLen = c1 + c3;
auto red = buildTable(c0, c2);
auto blue = buildTable(c1, c3);
vector<int> ans(2 * (n - 1) + 1);
auto combine = [&](int redSegments, int blueSegments, int boundaryCost, int factor) {
if (redSegments <= 0 || blueSegments <= 0) return;
if (redSegments >= (int)red.size() || blueSegments >= (int)blue.size()) return;
const auto &R = red[redSegments];
const auto &B = blue[blueSegments];
for (int i = 0; i < (int)R.size(); i++) {
if (R[i] == 0) continue;
for (int j = 0; j < (int)B.size(); j++) {
if (B[j] == 0) continue;
int oddness = 2 * (i + j) + boundaryCost;
if (oddness >= (int)ans.size()) continue;
int ways = mulMod(R[i], B[j]);
ways = mulMod(ways, factor);
addMod(ans[oddness], ways);
}
}
};
for (int s = 1; s <= min(redLen, blueLen); s++) {
combine(s, s, 2 * s - 1, 2);
}
for (int s = 1; s <= redLen && s + 1 <= blueLen; s++) {
combine(s, s + 1, 2 * s, 1);
}
for (int s = 1; s + 1 <= redLen && s <= blueLen; s++) {
combine(s + 1, s, 2 * s, 1);
}
for (int i = 0; i < (int)ans.size(); i++) {
if (i) cout << ' ';
cout << ans[i];
}
cout << char(10);
}
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
const int MOD = 1000000007;
const int MAXN = 800;
int C[MAXN + 1][MAXN + 1];
void addMod(int &a, int b) {
a += b;
if (a >= MOD) a -= MOD;
}
int mulMod(int a, int b) {
return int(1LL * a * b % MOD);
}
void precomputeCombinations() {
for (int i = 0; i <= MAXN; i++) {
C[i][0] = C[i][i] = 1;
for (int j = 1; j < i; j++) {
C[i][j] = C[i - 1][j];
addMod(C[i][j], C[i - 1][j - 1]);
}
}
}
vector<vector<int>> buildTable(int x, int y) {
int len = x + y;
vector<vector<int>> table(len + 2, vector<int>(len + 1));
auto addRuns = [&](int rx, int ry, int mult, int runs) {
if (rx <= 0 || ry <= 0 || rx > x || ry > y) return;
int base = mulMod(C[x - 1][rx - 1], C[y - 1][ry - 1]);
base = mulMod(base, mult);
int different = runs - 1;
int same = len - runs;
for (int cutDiff = 0; cutDiff <= different; cutDiff++) {
int waysDiff = mulMod(base, C[different][cutDiff]);
int keptSwitches = different - cutDiff;
for (int cutSame = 0; cutSame <= same; cutSame++) {
int segments = cutDiff + cutSame + 1;
int ways = mulMod(waysDiff, C[same][cutSame]);
addMod(table[segments][keptSwitches], ways);
}
}
};
for (int runs = 2; runs <= len; runs++) {
if (runs % 2 == 0) {
addRuns(runs / 2, runs / 2, 2, runs);
} else {
addRuns((runs + 1) / 2, runs / 2, 1, runs);
addRuns(runs / 2, (runs + 1) / 2, 1, runs);
}
}
return table;
}
int main() {
setIO();
precomputeCombinations();
int T;
cin >> T;
while (T--) {
int c0, c1, c2, c3;
cin >> c0 >> c1 >> c2 >> c3;
int n = c0 + c1 + c2 + c3;
int redLen = c0 + c2;
int blueLen = c1 + c3;
auto red = buildTable(c0, c2);
auto blue = buildTable(c1, c3);
vector<int> ans(2 * (n - 1) + 1);
auto combine = [&](int redSegments, int blueSegments, int boundaryCost, int factor) {
if (redSegments <= 0 || blueSegments <= 0) return;
if (redSegments >= (int)red.size() || blueSegments >= (int)blue.size()) return;
const auto &R = red[redSegments];
const auto &B = blue[blueSegments];
for (int i = 0; i < (int)R.size(); i++) {
if (R[i] == 0) continue;
for (int j = 0; j < (int)B.size(); j++) {
if (B[j] == 0) continue;
int oddness = 2 * (i + j) + boundaryCost;
if (oddness >= (int)ans.size()) continue;
int ways = mulMod(R[i], B[j]);
ways = mulMod(ways, factor);
addMod(ans[oddness], ways);
}
}
};
for (int s = 1; s <= min(redLen, blueLen); s++) {
combine(s, s, 2 * s - 1, 2);
}
for (int s = 1; s <= redLen && s + 1 <= blueLen; s++) {
combine(s, s + 1, 2 * s, 1);
}
for (int s = 1; s + 1 <= redLen && s <= blueLen; s++) {
combine(s + 1, s, 2 * s, 1);
}
for (int i = 0; i < (int)ans.size(); i++) {
if (i) cout << ' ';
cout << ans[i];
}
cout << char(10);
}
}