洛谷P2490 [SDOI2011]黑白棋(K-NIM)
阿新 • • 發佈:2021-11-03
開始漸漸理解博弈
首先提出一個結論:在最優策略下,白棋只會向右走,黑棋只會向左走
證明 :
若白棋向左走,那黑棋可以向左走相同的步數,但此時白棋的活動範圍減少,顯然會劣,黑棋同理
接下來考慮模型轉換
把每一對棋子之間的點數看做石子數,那本題就是經典的 k-NIM 博弈
不妨解釋一下 K-NIM 博弈
K-NIM 博弈
定義:共有 \(n\) 堆石子,每次可以從任意不超過 \(k\) 堆石子中拿走任意石子
在某次無法操作時,該人失敗,遊戲結束
先手必敗:
當且僅當把每堆石子的數量轉換成二進位制後,每個二進位制位上的和 %(k+1) 後均等於零
先手必勝:
只要不滿足上述情況,均為先手必勝局面
解釋一下為什麼膜 \((k+1)\)
考慮多餘的部分,對於 \([0,k]\)
若對面正處於必敗局面,在對面操作後,更改的範圍一定為 \([0,k]\)
那次者則可操作對應的步數 \([k,0]\) 使其重返必敗局面
所以要膜 \((k+1)\)
證明:
首先所有堆石子數量均為0的狀態一定為必敗局面,此時顯然滿足先手必敗
如果存在堆石子數量不為0,但滿足先手必敗局面,在對面操作完後,次者一定可以操作對應的石子數量使其重返必敗局面
假設在修改完其中一堆石子後二進位制最高位不為0的位數為m,那一定可以拿走對應的石子數量使其最高位變為0
因此滿足先手必敗,反之同理
轉移就很好寫了
令 \(dp_{i,j}\)
列舉哪幾堆新增石子,則: \[dp_{i+1,j+(1<<i)*x*(k+1)}+=dp_{i,j}\binom{\frac{m}{2}}{x*(k+1)} \]
因為轉移的堆數是無序的,於是
\[ans+=dp_{maxi,j}\binom{n-i-\frac{m}{2}}{\frac{m}{2}} \]\[ans=\binom{n}{k}-ans \]Code
#include <bits/stdc++.h> #define re register #define int long long #define pir make_pair #define fr first #define sc second #define db double using namespace std; const int mol=1e9+7; const int maxn=1e7+10; inline int qpow(int a,int b) { int ans=1; while(b) { if(b&1) (ans*=a)%=mol; (a*=a)%=mol; b>>=1; } return ans; } inline int read() { int s=0,w=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') w=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { s=s*10+ch-'0'; ch=getchar(); } return s*w; } int n,k,m,fac[10010],inv[10010]; inline int C(int n,int m) { if(n<m) return 0; return fac[n]*inv[m]%mol*inv[n-m]%mol; } int f[22][100010]; signed main(void) { freopen("erp.in","r",stdin); freopen("erp.out","w",stdout); n=read(),k=read(),m=read(); fac[0]=1; for(re int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mol; inv[n]=qpow(fac[n],mol-2); for(re int i=n;i>=1;i--) inv[i-1]=inv[i]*i%mol; f[0][0]=1; for(re int i=0;i<=20;i++) for(re int j=0;j<=n;j++) for(re int x=0;j+(1<<i)*x*(m+1)<=n-k&&x*(m+1)<=k/2;x++) { (f[i+1][j+(1<<i)*x*(m+1)]+=f[i][j]*C(k/2,x*(m+1))%mol)%=mol; } int ans=0; for(re int i=0;n-i-k/2>=0;i++) (ans+=f[21][i]*C(n-i-k/2,k/2)%mol)%=mol; printf("%lld\n",(C(n,k)-ans+mol)%mol); }