SRM597Hard
题意
给 \(2\ \times\ M\) 的网格每个格子 RGB 染色。要求相邻的不同色,每个 \(2\ \times\ 2\) 的矩形中三种颜色都要出现。
问用 \(R\) 个红色,\(G\) 个绿色,\(B\) 个蓝色有多少种方案染色,对 \(10^9\ +\ 7\) 取模。
\(1\ \leq\ M\ \leq\ 10^6,\ R\ +\ G\ +\ B\ =\ 2M\)
做法1
考虑第 \(i\) 列的颜色,如果第 \(i\ -\ 1\) 列定了,则第 \(i\) 列的方案就只有两种。所以考虑用每一列未出现的颜色代表这一列的染色方案,可以发现所求即为用 \(R\) 个红色,\(G\) 个绿色,\(B\) 个蓝色染 \(1\ \times\ M\) 的网格并且相邻不同色的方案数。
考虑先把 \(R\) 填好,然后向 \(R\) 中间塞 \(GB\) 的序列。可以发现只有四种情况:\(GBGB,\ GBG,\ BGBG,\ BGB\)。其中 \(GBGB\) 和 \(BGBG\) 消耗 \(G\) 和 \(B\) 数量一样多。
设有 \(x_0\) 个 \(GBGB\) 或 \(BGBG\),\(x_{-1}\) 个 \(GBG\),\(x_1\) 个 \(BGB\)。知道 \(x_0\) 就可以解出 \(x_{-1}\) 和 \(x_1\)。枚举 \(x_0\) 计算答案即可。
做法2
同样转化问题成 \(1\ \times\ M\)。
考虑 \(R\) 和 \(G\) 填好后,枚举有多少个相邻两个相等的位置,组合数计数即可。
代码
#line 2 "LittleElephantAndBoard.cpp"
#include <bits/stdc++.h>
#ifdef DEBUG
#define debug(...) fprintf(stderr, __VA_ARGS__)
#else
#define debug(...)
#endif
#ifdef __WIN32
#define LLFORMAT "I64"
#define Rand() ((rand() << 15) + rand())
#else
#define LLFORMAT "ll"
#define Rand() (rand())
#endif
using namespace std;
class LittleElephantAndBoard {
public:
int getNumber(int M, int R, int G, int B) ;
// BEGIN CUT HERE
public:
void run_test(int Case) { if ((Case == -1) || (Case == 0)) test_case_0(); if ((Case == -1) || (Case == 1)) test_case_1(); if ((Case == -1) || (Case == 2)) test_case_2(); if ((Case == -1) || (Case == 3)) test_case_3(); }
private:
template <typename T> string print_array(const vector<T> &V) { ostringstream os; os << "{ "; for (typename vector<T>::const_iterator iter = V.begin(); iter != V.end(); ++iter) os << '\"' << *iter << "\","; os << " }"; return os.str(); }
void verify_case(int Case, const int &Expected, const int &Received) { cerr << "Test Case #" << Case << "..."; if (Expected == Received) cerr << "PASSED" << endl; else { cerr << "FAILED" << endl; cerr << "\tExpected: \"" << Expected << '\"' << endl; cerr << "\tReceived: \"" << Received << '\"' << endl; } }
void test_case_0() { int Arg0 = 2; int Arg1 = 2; int Arg2 = 1; int Arg3 = 1; int Arg4 = 4; verify_case(0, Arg4, getNumber(Arg0, Arg1, Arg2, Arg3)); }
void test_case_1() { int Arg0 = 2; int Arg1 = 2; int Arg2 = 2; int Arg3 = 0; int Arg4 = 0; verify_case(1, Arg4, getNumber(Arg0, Arg1, Arg2, Arg3)); }
void test_case_2() { int Arg0 = 10; int Arg1 = 7; int Arg2 = 7; int Arg3 = 6; int Arg4 = 496; verify_case(2, Arg4, getNumber(Arg0, Arg1, Arg2, Arg3)); }
void test_case_3() { int Arg0 = 474; int Arg1 = 250; int Arg2 = 300; int Arg3 = 398; int Arg4 = 969878317; verify_case(3, Arg4, getNumber(Arg0, Arg1, Arg2, Arg3)); }
// END CUT HERE
} ___test;
// BEGIN CUT HERE
int main() {
int test_case;
scanf("%d", &test_case);
___test.run_test(test_case);
}
// END CUT HERE
//----------head----------
const int maxn = 1e6 + 10, mod = 1e9 + 7;
int fac[maxn], ifac[maxn];
int pow_mod(int x, int n = mod - 2) { int y = 1; while(n) { if(n & 1) y = (long long) y * x % mod; x = (long long) x * x % mod; n >>= 1; } return y; }
int C(int n, int m) { if(n < 0 || m < 0 || n - m < 0) return 0; return (long long) fac[n] * ifac[m] % mod * ifac[n - m] % mod; }
int LittleElephantAndBoard::getNumber(int M, int R, int G, int B) {
int r, g, b, s = R + B + G >> 1;
r = s - R; g = s - G; b = s - B;
if(r < 0 || b < 0 || g < 0) return 0;
fac[0] = 1; for (int i = 1; i <= 1000000; ++i) fac[i] = (long long) fac[i - 1] * i % mod;
ifac[1000000] = pow_mod(fac[1000000]); for (int i = 1000000; i; --i) ifac[i - 1] = (long long) ifac[i] * i % mod;
int ans = 0;
if(r < g) swap(r, g); if(g < b) swap(g, b); if(r < g) swap(r, g);
for (int fl = 0; fl < 2; ++fl) for (int fr = 0; fr < 2; ++fr) for (int i = 0; i < r; ++i) {
int x = (long long) ((g == 0) ? (i + fl + fr == 0) : C(g - 1, i + fl + fr - 1)) * C(r - 1, i) % mod;
if(x) {
int s = r - 1 - i + g - 1 - (i + fl + fr - 1), t = r + g + 1 - s;
if(!b) { if(!s) ans = (ans + x) % mod; }
else {
if(b >= s) ans = ((long long) x * C(t, b - s) + ans) % mod;
}
}
}
return (ans * 2) % mod;
}