bzoj2281 黑白棋 博弈论

该博客探讨了bzoj2281题目的黑白棋问题,通过将黑子和白子配对转化为火柴问题,进一步转换为Nim游戏的必胜策略。博主介绍了当二进制位的抑或值模d+1等于0时为必败状态的结论,并给出动态规划求解必败状态方案数的方法。最后,通过计算所有必败状态来确定必胜状态的数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

       这道题目的转化真是巧妙啊。不过据说题目有点问题,那就不管了。首先将第i个白子和第i个黑子组成一对。那么这一对中,白子的左移是没有意义的,黑子的右移也是没有意义的(虽然有反例,但好像只能这样了)。那么每一对黑子和白子可以看成一堆火柴,则问题转化为一次在d堆中拿火柴的必胜策论。

       对于d-Nim(就这么叫了)问题,有个结论是如果对于二进制的每一位的抑或值x都有x≡0(mod d+1),那么为必败状态,否则为必胜状态。证明不会。。

       然后就可以用f[i][j]表示到二进制的第i位(从低到高),目前总和为j的必败状态方案数。注意j<=N-K。那么f[i][j]可以转移到f[i+1][j+x*(d+1)*2^(i)],注意有C(K/2,x*(d+1))种组合方式

       最后统计必胜状态。用所有的状态C(N,K)减去所有的必败状态。在必败状态中,如果两端的点都确定了,那么j就确定了。不妨枚举这个j,然后在组合一下点的排列方式即可。

AC代码如下:

#include<iostream>
#include<cstdio>
#define ll long long
#define mod 1000000007
using namespace std;
 
int n,m,d,bin[25];
ll c[20005][205],f[25][20005];
ll C(int aa,int bb){
    if (bb*2>aa) return c[aa][aa-bb]; else return c[aa][bb];
}
int main(){
    scanf("%d%d%d",&n,&m,&d); int i,j;
    bin[0]=1; for (i=1; i<=15; i++) bin[i]=(bin[i-1]<<1)%mod;
    for (i=0; i<=n; i++){
        c[i][0]=1;
        for (j=1; j<=m && j<=i; j++) c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
    }
    f[0][0]=1;
    for (i=0; i<15; i++)
        for (j=0; j<=n-m; j++)
            for (int k=0; k*(d+1)<=m/2; k++){
                int x=j+k*(d+1)*bin[i]; if (x>n-m) break;
                f[i+1][x]=(f[i+1][x]+f[i][j]*C(m/2,k*(d+1)))%mod;
            }
    ll ans=0;
    for (i=0; i<=n-m; i++) ans=(ans+f[15][i]*C(n-m/2-i,m/2))%mod;
    printf("%lld\n",(C(n,m)-ans+mod)%mod);
    return 0;
}

by lych

2015.12.5

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值