BZOJ2281 黑白棋
BZOJ2281 黑白棋
1 题外话
博弈和DP不分家是吧
2 sol
由于先手不能左移,后手不能右移,那么如果两个棋子紧靠,双方都不能移动,且不影响其他棋子的移动。
将白-黑之间的间隔视为石子,问题转换成在(n/2) 个石子堆中任取(1,2,...,d) 个石子堆,每个可以取任意个
这是一个典型的k-nim问题,这里有详细阐述
给出结论:必败态是把每堆石子转换为二进制后,其中每一位上为1的石子堆数都能被(d+1)整除
那么我们设(f[i][j]) 表示考虑二进制前i位,放置了j个石头的方案数
转移如下
[f[i+1][j+k imes (d+1) imes 2^i]+=f[i][j]*C^{frac{K}{2}}_{k imes (d+1)}]
总方案减去必败即为必胜
3 code
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define int long long using namespace std; const int N=100010; const int mod=1000000007; inline void read(int &x) { x=0; int f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if (ch=='-') { f=-1; } ch=getchar(); } while(ch>='0'&&ch<='9') { x=x*10+ch-'0'; ch=getchar(); } x*=f; } int n,k,d; int C[N][210]; int f[30][N]; int bin[30]; inline void pre() { for(int i=0;i<=n;i++) { C[i][0]=1; } for(int i=1;i<=n;i++) { for(int j=1;j<=min(2*k,i);j++) { C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod; } } bin[0]=1; for(int i=1;i<=20;i++) { bin[i]=bin[i-1]<<1; } } inline int c(int n,int m) { if (m>n-m) { m=n-m; } return C[n][m]; } signed main() { read(n),read(k),read(d); k/=2; pre(); f[0][0]=1; for(int i=0;i<15;i++) { for(int j=0;j<=n-2*k;j++) { for(int g=0;g*(d+1)<=k&&j+g*(d+1)*bin[i]<=n-2*k;g++) { f[i+1][j+g*(d+1)*bin[i]]+=f[i][j]*c(k,g*(d+1)); f[i+1][j+g*(d+1)*bin[i]]%=mod; } } } int ans=c(n,k*2); for(int i=0;i<=n-2*k;i++) { ans-=(f[15][i]*c(n-i-k,k))%mod; ans=((ans%mod)+mod)%mod; } ans=((ans%mod)+mod)%mod; printf("%lld ",ans); return 0; }
Created: 2021-08-13 周五 11:25