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;
}
高考结束,重新回归。