2368. 黑白棋

版权声明:本人初三蒟蒻一只,欢迎各位大佬来点评。 https://blog.csdn.net/HiChocolate/article/details/84895007

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.


猜你喜欢

转载自blog.csdn.net/HiChocolate/article/details/84895007
今日推荐