[SDOI2011]黑白棋
Description
小A和小B又想到了一个新的游戏。
这个游戏是在一个1*n的棋盘上进行的,棋盘上有k个棋子,一半是黑色,一半是白色。
最左边是白色棋子,最右边是黑色棋子,相邻的棋子颜色不同。
小A可以移动白色棋子,小B可以移动黑色的棋子,他们每次操作可以移动1到d个棋子。
每当移动某一个棋子时,这个棋子不能跨越两边的棋子,当然也不可以出界。当谁不可以操作时,谁就失败了。
小A和小B轮流操作,现在小A先移动,有多少种初始棋子的布局会使他胜利呢?
Input
共一行,三个数,n,k,d。
Output
输出小A胜利的方案总数。答案对1000000007取模。
Sample Input
10 4 2
Sample Output
182
HINT
1<=d<=k<=n<=10000, k为偶数,k<=100。
考虑黑白棋是相间的
那么第i个白棋和第i个黑棋之间距离为dis,那么我们看做一堆石头数为
dis的堆
一次操作等价于移除不超过d堆的任意石子
这就是Nimk游戏
先手必败当且仅当所有石子数的二进制每一位为1的个数都能被(d+1)整除
用所有情况减去不合法的情况
于是DP
令$f[i][j]$表示第i位,石子数和为j的不合法方案数
$f[i+1][j+x*(d+1)*(1<<i)]+=f[i][j]$
石子数和<=n-k,堆数k/2
接下来统计答案
$ans=C_{n}^{k}-\sum_{i=0}^{n-k}f[16][i]*C_{n-k-i+k/2}^{k/2}$
后面那个组合数是分配剩下的空格在每对棋子中间
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 typedef long long lol; 8 lol C[10001][101],Mod=1e9+7; 9 lol f[17][10001],n,k,d,pw[17],ans; 10 int main() 11 {lol i,j,l; 12 cin>>n>>k>>d; 13 pw[0]=1; 14 for (i=1;i<=16;i++) 15 pw[i]=pw[i-1]*2; 16 f[0][0]=1; 17 C[1][0]=C[1][1]=1; 18 for (i=2;i<=n;i++) 19 { 20 C[i][0]=1; 21 for (j=1;j<=min(i,k);j++) 22 { 23 C[i][j]=(C[i-1][j-1]+C[i-1][j])%Mod; 24 } 25 } 26 for (i=0;i<16;i++) 27 { 28 for (j=0;j<=n-k;j++) 29 { 30 for (l=0;l*(d+1)<=k/2&&l*(d+1)*pw[i]+j<=n-k;l++) 31 { 32 f[i+1][j+l*(d+1)*pw[i]]+=f[i][j]*C[k/2][l*(d+1)]%Mod; 33 f[i+1][j+l*(d+1)*pw[i]]%=Mod; 34 } 35 } 36 } 37 ans=C[n][k]; 38 for (i=0;i<=n-k;i++) 39 ans=(ans-f[16][i]*C[n-i-k+k/2][k/2]%Mod+Mod)%Mod; 40 cout<<ans; 41 }