2368. 黑白棋
Description
小A和小B又想到了一個新的遊戲。
這個遊戲是在一個1*n的棋盤上進行的,棋盤上有k個棋子,一半是黑色,一半是白色。
最左邊是白色棋子,最右邊是黑色棋子,相鄰的棋子顏色不同。
小A可以移動白色棋子,小B可以移動黑色的棋子,他們每次操作可以移動1到d個棋子。
每當移動某一個棋子時,這個棋子不能跨越兩邊的棋子,當然也不可以出界。當誰不可以操作時,誰就失敗了。
小A和小B輪流操作,現在小A先移動,有多少種初始棋子的佈局會使他勝利呢?
Input
共一行,三個數,n,k,d。
Output
輸出小A勝利的方案總數。答案對1000000007取模。
Sample Input
10 4 2
Sample Output
182
Data Constraint
對於30%的資料,有 k=2。
對於100%的資料,有1<=d<=k<=n<=10000, k為偶數,k<=100。
題解
我們首先發現這題直接做不好做。
那麼找找有什麼突破口。
我們發現,當每個白棋右邊都貼著個黑棋,那麼先手必敗。
那麼白棋黑棋就都是要儘量往左邊白棋或右邊黑棋靠。
我們發現,一個白棋往左移或一個黑棋往右走都沒有意義,因為越往右邊(左邊)走越對黑棋(白棋)不利。
那麼我們就可以看到左邊白棋對應右邊黑棋中間這一段很重要。
為了好表示,把紅色格子連成的塊說成關鍵堆,格子為石子。
在此,我們可以發現,每次黑棋或白棋走一格,都是縮短了關鍵堆的大小。
那麼,我們就得到了k/2個關鍵堆,每次相當於取出其中1~d個堆中任意一個石子。
沒得取時失敗。
這就是一個典型的K·NIM遊戲。
具體介紹可以看到:https://blog.csdn.net/weixinding/article/details/7321139
於是我們得到了個結論:
當每個關鍵堆的大小用二進位制表示出來時,對於每個位置上的1的個數,如果%(d+1)=0,那麼就表示當前狀態必敗。
差值表示一下就可以發現,必勝態就是總狀態-必敗態
總狀態就是C(k,n)
接下來我們考慮如何求必敗態。
我們設f[i,j]表示當前把k/2個關鍵堆二進位制表示出來後,組成1~i位滿足必敗時用了j個空格。
於是乎,考慮轉移。
我們發現,轉移到下一位時,就是要把下一位的1的個數變成(d+1)的倍數。
那麼列舉l表示下一位有l*(d+1)個1。
然後由於可以隨意擺放1的位置,那麼乘上一個組合數C(l*(d+1),k/2)即可。
轉移方程:
然而這樣算完之後,我們還要考慮如何在n中插入這幾個堆。
那麼列舉堆的總大小j。
我們把上圖中堆的組成部分給刪掉。
得到
個空格,那麼問題就轉化為在這麼多個空格中插入k/2擋板
方案為
最後答案轉移:
減一減即可。
程式碼:
uses math;
var
i,j,k,l,n,m,d,s1,s2,now:longint;
ans,gs:int64;
mo:int64=1000000007;
f:array[0..18,0..20000] of int64;
g:array[1..10000,1..100] of int64;
jc:array[1..10000] of int64;
mi:array[0..10000] of int64;
zhs:array[0..10000,0..300] of int64;
function qsm(a,b:int64):int64;
var
t,y:int64;
begin
t:=1;
y:=a;
while b<>0 do
begin
if(b and 1)=1 then
t:=(t*y) mod mo;
y:=(y*y) mod mo;
b:=b shr 1;
end;
exit(t);
end;
function c(n,m:int64):int64;
var
i,j,k,l:int64;
begin
if (n=m) or (m=0) or (n=0) then exit(1);
if n>m then
begin
l:=n;
n:=m;
m:=l;
end;
i:=jc[m];
j:=(jc[n]*jc[m-n]) mod mo;
c:=i*qsm(j,mo-2) mod mo;
end;
begin
jc[1]:=1;
for i:=2 to 10000 do
begin
ans:=i;
jc[i]:=(jc[i-1]*ans) mod mo;
end;
mi[0]:=1;
for i:=1 to 100 do mi[i]:=(mi[i-1]*2) mod mo;
zhs[0,0]:=1;
for i:=1 to 10000 do
begin
zhs[i,0]:=1;
for j:=1 to 300 do
begin
zhs[i,j]:=(zhs[i-1,j-1]+zhs[i-1,j]) mod mo;
end;
end;
ans:=0;
readln(n,m,d);
f[0,0]:=1;
ans:=c(m,n);
for i:=0 to 17 do
begin
for j:=0 to n-m do
begin
for k:=0 to n-k do
begin
if (mi[i]*k*(d+1)>n-k) or (k*(d+1)>m div 2) then break
else
begin
f[i+1,j+mi[i]*k*(d+1)]:=(f[i+1,j+mi[i]*k*(d+1)]
+f[i,j]*zhs[m div 2,k*(d+1)]) mod mo;
end;
end;
end;
end;
for i:=0 to n-m do
begin
f[18,i]:=(f[18,i]*zhs[n-m div 2-i,m div 2]) mod mo;
ans:=(ans-f[18,i]+mo) mod mo;
end;
writeln(ans);
end.
end.