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;
}
posted @ 2018-09-01 17:01  King_George  阅读(240)  评论(0编辑  收藏  举报