【題解】【BZOJ】BZOJ2281 黑白棋
阿新 • • 發佈:2021-08-13
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\times (d+1)\times 2^i]+=f[i][j]*C^{\frac{K}{2}}_{k\times (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\n",ans); return 0; }
Created: 2021-08-13 週五 11:25