【BZOJ2281】[SDOI2011]黑白棋(博弈论,动态规划)
【BZOJ2281】[SDOI2011]黑白棋(博弈论,动态规划)
题面
题解
先看懂这题目在干什么。
首先BZOJ上面的题面没有图,换到洛谷看题就有图了。
不难发现都相邻的两个异色棋子放在一起的时候,此时的先手无论怎么动,后手直接把棋子靠上去,这样子一定是先手先无法移动。即先手必败。
把相邻的黑白棋子配对,不难发现这个玩意就是一个\(NimK\)游戏了。
考虑\(NimK\)游戏是怎么来的,即把每堆石子转为二进制之后,检查是否每一位上的棋子数量都是\(K+1\)的倍数,如果是,则先手必败。否则先手必胜。
那么这样子可以\(dp\)了。
设\(f[i][j]\)表示当前考虑到了二进制上的第\(i\)位,总共放了\(j\)个石子的先手必败的方案数。
这样子用总的放置方案数减去必败的方案数就可以得到必胜的方案数了。
#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long
#define MAX 10010
#define MOD 1000000007
void add(int &x,int y){x+=y;if(x>=MOD)x-=MOD;}
int jc[MAX],jv[MAX],inv[MAX];
int C(int n,int m){if(m>n)return 0;return 1ll*jc[n]*jv[m]%MOD*jv[n-m]%MOD;}
int f[15][MAX],n,K,d,ans;
int main()
{
scanf("%d%d%d",&n,&K,&d);
n-=K;K>>=1;jc[0]=jv[0]=inv[0]=inv[1]=1;
for(int i=1;i<=n+K+K;++i)jc[i]=1ll*jc[i-1]*i%MOD;
for(int i=2;i<=n+K+K;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
for(int i=1;i<=n+K+K;++i)jv[i]=1ll*jv[i-1]*inv[i]%MOD;
f[0][0]=1;
for(int j=0;j<=13;++j)
for(int i=0;i<=n;++i)
if(f[j][i])
for(int k=0;k<=K;k+=d+1)
if(i+(1<<j)*k<=n)add(f[j+1][i+(1<<j)*k],1ll*f[j][i]*C(K,k)%MOD);
for(int i=0;i<=n;++i)add(ans,1ll*f[14][i]*C(n-i+K,K)%MOD);
ans=(C(n+2*K,2*K)+MOD-ans)%MOD;
printf("%d\n",ans);
return 0;
}