[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 }
原文地址:https://www.cnblogs.com/Y-E-T-I/p/8637105.html