Description
Input
Output
输出小A胜利的方案总数。答案对1000000007取模。
Sample Input
Sample Output
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 }