• 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+2il(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。
    在这里插入图片描述
    我们把上图中堆的组成部分给删掉。
    得到njk/2n-j-k/2个空格,那么问题就转化为在这么多个空格中插入k/2挡板
    方案为C(k/2,njk/2)C(k/2,n-j-k/2)
    最后答案转移:ans:=f[j]C(k/2,njk/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.
    
    
    
    我活在这夜里。无论周围多么黑暗,我都要努力发光!我相信着,终有一天,我会在这深邃的夜里,造就一道最美的彩虹。
  • 相关阅读:
    FCKeditor firefox Ajax提交,内容为空.解决.
    Javascript:Go to top of page
    js实现两级联动下拉列表
    php+mysql实现二级联动下拉列表
    ajax 实现两级级联下拉列表
    SharpDevelop_3.2.1.6466_Setup软件安装汉化
    c#拓展项目作业
    摇色子(两颗色子)
    C#编程实践51题目解答
    编程实践53
  • 原文地址:https://www.cnblogs.com/RainbowCrown/p/11148382.html
Copyright © 2020-2023  润新知