bzoj 2281: [Sdoi2011]黑白棋 博弈論+動態規劃+排列組合
阿新 • • 發佈:2019-02-10
題意
小A和小B又想到了一個新的遊戲。
這個遊戲是在一個1*n的棋盤上進行的,棋盤上有k個棋子,一半是黑色,一半是白色。
最左邊是白色棋子,最右邊是黑色棋子,相鄰的棋子顏色不同。
小A可以移動白色棋子,小B可以移動黑色的棋子,他們每次操作可以移動1到d個棋子。
每當移動某一個棋子時,這個棋子不能跨越兩邊的棋子,當然也不可以出界。當誰不可以操作時,誰就失敗了。
小A和小B輪流操作,現在小A先移動,有多少種初始棋子的佈局會使他勝利呢?
1<=d<=k<=n<=10000, k為偶數,k<=100。
答案對1000000007取模。
分析
顯然先手的必敗局面就是每一對棋子都靠一起,這樣的話無論先手怎麼動,後手只要跟著模仿,最後先手總會沒有路走。
那麼我們可以把每對棋子之間的空格子看作是石子,那麼這就轉換成了一個拓展版的Nim遊戲:有n堆石子,每次可以從其中d堆中任意取若干個石子,不能取的一方算輸。
而這個遊戲先手必敗當且僅當把n堆石子的數量全部轉化為二進位制後,每一位的1的個數都是(d+1)的倍數,否則必勝。證明比較複雜,可以 點這裡。
有了這個結論這題就好做了。
我們從二進位制低位往高位dp。設f[i,j]表示dp到第i位,當前總共放了j顆石子的必敗局面方案數。那麼
那麼答案就等於
程式碼
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=10005;
const int MOD=1000000007;
int n,k,d,f[20][N],bin[20],ny[N],jc[N];
int ksm(int x,int y)
{
int ans=1;
while (y)
{
if (y&1) ans=(LL)ans*x%MOD;
x=(LL)x*x%MOD;y>>=1;
}
return ans;
}
int C(int n,int m)
{
return (LL)jc[n]*ny[m]%MOD*ny[n-m]%MOD;
}
int main()
{
scanf("%d %d%d",&n,&k,&d);
bin[0]=1;
for (int i=1;i<=16;i++) bin[i]=bin[i-1]*2;
jc[0]=ny[0]=1;
for (int i=1;i<=n;i++) jc[i]=(LL)jc[i-1]*i%MOD,ny[i]=ksm(jc[i],MOD-2);
f[0][0]=1;
for (int i=1;i<=15;i++)
for (int j=0;j<=n-k;j++)
for (int l=0;l*(d+1)<=k/2&&l*(d+1)*bin[i-1]<=j;l++)
(f[i][j]+=(LL)f[i-1][j-l*(d+1)*bin[i-1]]*C(k/2,l*(d+1))%MOD)%=MOD;
int ans=0;
for (int i=0;i<=n-k;i++) (ans+=(LL)f[15][i]*C(n-k/2-i,k/2)%MOD)%=MOD;
ans=(C(n,k)-ans+MOD)%MOD;
printf("%d",ans);
return 0;
}