bzoj2281 [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。

 

正解:博弈论+$dp$。

推荐博客:$nim$游戏及其变形

首先题目似乎漏了一句话,就是白棋不能右移,黑棋不能左移。。

因为每对黑白棋相邻,所以实际上我们可以转化一下模型,每对黑白棋就是一堆石子,它们的距离$-1$就是石子个数。

现在每次可以取$d$个石子堆中的石子,问先手必胜的方案有多少?

这是一个经典的$nim-k$问题,结论是对于石子堆的每一个二进制位,如果这一位上为$1$的石子堆数是$d+1$的倍数,那么先手必败。

所以我们可以直接考虑先手必败的方案数,再用总方案数减去即可。

设$f[i][j]$表示考虑到二进制从小到大的第$i$位,当前石子总数为$j$,先手必败的方案数。

那么$f[i+1][j+p*(d+1)*2^{i}]+=f[i][j]*\binom{k/2}{p*(d+1)}$,注意第二维必须$\leq n-k$。

最后我们枚举石子数$i$,那么$Ans-=f[15][i]*\binom{n-k/2-i}{k/2}$,这里可以看成在合法位置中选择$k/2$个位置的方案数。

 

 1 #include <bits/stdc++.h>
 2 #define il inline
 3 #define RG register
 4 #define ll long long
 5 #define rhl (1000000007)
 6 
 7 using namespace std;
 8 
 9 int c[10005][105],f[16][10005],n,k,d,ans;
10 
11 il int gi(){
12   RG int x=0,q=1; RG char ch=getchar();
13   while ((ch<'0' || ch>'9') && ch!='-') ch=getchar();
14   if (ch=='-') q=-1,ch=getchar();
15   while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar();
16   return q*x;
17 }
18 
19 il int C(RG int n,RG int m){
20   return n<m ? 0 : c[n][min(m,n-m)];
21 }
22 
23 int main(){
24 #ifndef ONLINE_JUDGE
25   freopen("chess.in","r",stdin);
26   freopen("chess.out","w",stdout);
27 #endif
28   n=gi(),k=gi(),d=gi(),c[0][0]=1;
29   for (RG int i=1;i<=n;++i){
30     c[i][0]=1;
31     for (RG int j=1;j<=k && j<=i;++j){
32       c[i][j]=c[i-1][j-1]+c[i-1][j];
33       if (c[i][j]>=rhl) c[i][j]-=rhl;
34     }
35   }
36   f[0][0]=1,ans=C(n,k);
37   for (RG int i=0;i<15;++i)
38     for (RG int j=0;j<=n-k;++j)
39       for (RG int p=0,q;p*(d+1)<=(k>>1);++p){
40     q=j+p*(d+1)*(1<<i); if (q>n-k) break;
41     f[i+1][q]=(1LL*f[i][j]*C(k/2,p*(d+1))+f[i+1][q])%rhl;
42       }
43   for (RG int i=0;i<=n-k;++i){
44     ans=(-1LL*f[15][i]*C(n-k/2-i,k/2)+ans)%rhl;
45     if (ans<0) ans+=rhl;
46   }
47   cout<<ans; return 0;
48 }

 

posted @ 2017-12-31 19:40  wfj_2048  阅读(288)  评论(0编辑  收藏  举报