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;
}

转载于:https://www.cnblogs.com/mmlz/p/6388438.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值