• [SDOI2011]黑白棋 kth


    题面

    题面

    题解

    观察题目,我们可以发现,这个游戏其实就是不断再把对方挤到一边去,也就是黑子不断往左走,白子不断往右走。
    因此可以发现,如果将黑白子按顺序两两配对,那么它们中间的距离会不断缩小,且每次操作只能改变k对黑白子中间的距离,并且每次改变多少没有限制。
    那么这就变成了一个kth-nim游戏,因此我们把匹配的黑白子中间的距离当做石子个数,直接按照kth-nim游戏来做即可.
    输出方案则要用到DP。
    f[i][j]表示DP到二进制的第i位,放j个石头的不合法方案。
    那么根据nim游戏,为了保证不合法,我们就要保证二进制上的每一位的1的个数之和整除(k + 1)。
    因此我们枚举现在一共已经用了多少个石子,然后枚举当前位(二进制第i位)要用到多少石子。
    再用组合数来计算分配方案即可。

    // luogu-judger-enable-o2
    #include<bits/stdc++.h>
    using namespace std;
    #define R register int
    #define p 1000000007
    #define AC 20100
    #define ac 20
    #define LL long long
    
    LL n, k, d, ans;
    LL fac[AC], inv[AC], f[ac][AC], bits[ac];//f[i][j]表示二进制前i位,放j个石子的方案数
    
    LL C(int x, int y){return fac[x] * inv[y] % p * inv[x - y] % p;}
    void up(LL &x, LL y)
    {
        x += y;
        if(x < 0) x += p;
        if(x >= p) x -= p;
    }
    
    void pre()
    {
        scanf("%lld%lld%lld", &n, &k, &d);
        inv[0] = inv[1] = fac[0] = bits[1] = 1, k >>= 1;//注意0!= 1
        int tmp = n + k;
        for(R i = 2; i <= tmp; i ++) inv[i] = (p - p / i) * inv[p % i] % p;
        for(R i = 1; i <= tmp; i ++) //求出阶乘表和阶乘逆元表
            fac[i] = fac[i - 1] * i % p, inv[i] = inv[i] * inv[i - 1] % p;
        for(R i = 2; i <= 15; i ++) bits[i] = bits[i - 1] << 1;
    }
    
    void work()
    {
        int maxn = 15, all = n - 2 * k;
        f[0][0] = 1;
        for(R i = 1; i <= maxn; i ++)
        {
            for(R j = 0; j <= all; j ++)//枚举当前格子数
            {
                for(R l = 0; l <= k; l += d + 1)//枚举d + 1的倍数(相比于上次的增量),因为当前位1的个数必须是d + 1的倍数
                {//把这l份(d + 1)分配给每一堆石子,但不能拆开任意一堆(不然这个1就不在第i位了)
                    int have = j - l * bits[i];//上个格子的
                    if(have < 0) break;//且每堆要么分一个,要么不分,所以份数不能超过堆数
                    up(f[i][j], f[i - 1][have] * C(k, l) % p);//在k堆里面选l堆出来放		
                }
            }
        }//先枚举放了的石子个数,于是就要在剩下的n - i - 2 * k个格子中插入允许相邻的k堆石子 n - i - 2 * k + k = n - i - k
        for(R i = 0; i <= all; i ++) up(ans, -C(n - i - k, k) * f[15][i] % p);
        up(ans, C(n, k << 1));
        printf("%lld
    ", ans);
    }
    
    int main()
    {
    //	freopen("in.in", "r", stdin);
        pre();
        work();
        //fclose(stdin);
        return 0;
    }
    
  • 相关阅读:
    设计模式七大原则之单一职责原则
    机器学习入门与进阶
    Django之路
    Python编程之路
    Python练习1
    Docker入门与进阶
    运维相关
    Node.js(一)
    位运算
    双指针算法
  • 原文地址:https://www.cnblogs.com/ww3113306/p/10341970.html
Copyright © 2020-2023  润新知