poj3718 Facer's Chocolate Dream

题目戳这里

这是一道好题。首先我们将模型转换一下,就两个01串异或一下,得到新串\(S\)。现在就是求有有多少种选法,从空集合变成\(S\)。其实这到题目只跟\(S\)\(1\)的数目有关——因为所有巧克力是等价的。我们设\(f[m][T]\)表示前\(m\)次操作,使得集合中有\(T\)\(1\)的方案数。转移:

\[f[a][b] = \left( \sum_{i=0}^3f[a-1][b+2i-3] \times \binom{N-b}{i} \times \binom{b}{3-i}-f[a-2][b] \times \left(\binom{N}{3}-a+2 \right) \right)/a \]

我们现在来解释一下这个方程。

\[\sum_{i=0}^3f[a-1][b+2i-3] \times \binom{N-b}{i} \times \binom{b}{3-i} \]

我们枚举我们接下拿的那盘用\(i\)盘是从\(0\)变成\(1\)\(3-i\)盘是从\(1\)变成\(0\)(不管重复)。这里可能有人问为什么是\(\binom{N-b}{i}\)不是什么xxx(具体的我忘了)的。因为如果那样你就把\(1\)的位置也考虑进来了,这样是把\(1\)的位置固定了。

然后就是把重复的部分减去。我们不妨设第\(a\)次与\(a-1\)次重复了(反正是组合),我们就可以减去$$f[a-2][b] \times \binom{N}{3}-a+2$$

然后这一次可以发生在前\(a\)次中任何一次,故最后除以个\(a\)(因为我们在这里强加了次序,我们把它当作了排列,实际为组合)。

#include<cstdio>
#include<cstdlib>
using namespace std;

const int maxn = 1010,rhl = 10007;
int N,M,cnt,C[maxn][maxn],f[maxn][maxn],inv[maxn]; bool A[maxn],exist[maxn][maxn];

inline int qsm(int a,int b)
{
	int ret = 1;
	for (;b;b >>= 1,(a *= a) %= rhl) if (b & 1) (ret *= a) %= rhl;
	return ret;
}
inline void ready()
{
	for (int i = 1;i <= 1000;++i) inv[i] = qsm(i,rhl-2);
	C[0][0] = 1;
	for (int i = 1;i <= 1000;++i)
	{
		C[i][0] = 1;
		for (int j = 1;j <= i;++j)
		{
			C[i][j] = C[i-1][j-1]+C[i-1][j];
			if (C[i][j] >= rhl) C[i][j] -= rhl;
		}
	}
}

inline int dp(int a,int b)
{
	if (exist[a][b]) return f[a][b];
	if (a == 0) return 0;
	exist[a][b] = true;
	for (int i = 0;i <= 3;++i)
	{
		if (N-b < i||3-i > b) continue;
		f[a][b] += dp(a-1,b+2*i-3)*C[N-b][i]%rhl*C[b][3-i]%rhl;
		if (f[a][b] >= rhl) f[a][b] -= rhl;
	}
	f[a][b] -= f[a-2][b]*(C[N][3]-a+2)%rhl;
	if (f[a][b] < 0) f[a][b] += rhl;
	if (f[a][b] >= rhl) f[a][b] -= rhl;
	(f[a][b] *= inv[a]) %= rhl;
	return f[a][b];
}

int main()
{
	freopen("3718.in","r",stdin);
	freopen("3718.out","w",stdout);
	ready();
	while (true)
	{
		scanf("%d %d",&N,&M); if (!N && !M) break; cnt = 0;
		for (int i = 1,a;i <= N;++i) scanf("%1d",&a),A[i] = (bool)a;
		for (int i = 1,a;i <= N;++i) scanf("%1d",&a),cnt += (a^(int)A[i]);
		for (int i = 0;i <= M;++i) for (int j = 0;j <= N;++j) f[i][j] = exist[i][j] = 0;
		f[0][0] = 1; exist[0][0] = true;
		printf("%d\n",dp(M,cnt));
	}
	fclose(stdin); fclose(stdout);
	return 0;
}
posted @ 2017-02-10 23:46  lmxyy  阅读(179)  评论(0编辑  收藏  举报