1. 程式人生 > >2368. 黑白棋

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)即可。
轉移方程:
f

[ i + 1 , j + 2 i l ( d + 1 ) ] + = f [ i , j ] C ( l ( d + 1 ) , k / 2 ) f[i+1,j+2^i*l*(d+1)]+=f[i,j]*C(l*(d+1),k/2)

然而這樣算完之後,我們還要考慮如何在n中插入這幾個堆。
那麼列舉堆的總大小j。
在這裡插入圖片描述
我們把上圖中堆的組成部分給刪掉。
得到 n j k / 2 n-j-k/2 個空格,那麼問題就轉化為在這麼多個空格中插入k/2擋板
方案為 C ( k / 2 , n j k / 2 ) C(k/2,n-j-k/2)
最後答案轉移: a n s : = f [ j ] C ( k / 2 , n j k / 2 ) ans:=f[j]*C(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.