zoukankan      html  css  js  c++  java
  • HDU 2865 Birthday Toy

    HDU_2865

        由于有相同颜色不能相邻这一限制,我们就需要用dp去计算burnside引理中的C(f)了。

        按常规套路来,先将N的约数找到,接着对于每个任意约数R,就可以找到循环节数量为R的置换数了,然后需要用dp计算出对于有R个循环节的置换下的“不动方案”的数量。

        首先,中间的圆圈的颜色是任意的,而且对于每种颜色而言“不动方案”的数量是一致的,也就是说中间的圆圈有K个颜色可选,而我们只需要计算任意一个情况就可以了,最后乘K就是总数。

        接下来小圆圈就剩下K-1个颜色可选了,而我们需要计算长度为R的一串珠子的合法方案数,所谓合法就是相邻的珠子不能同色,以及首尾珠子不能同色。这时第1个珠子有K-1个颜色可选,而且对于其选择任意一种颜色,最终的结果都是一样的,所以我们只需计算其中一种情况,最后乘以K-1就可以了。

        比如我第1个珠子选了黄色,那么在dp的时候就可以用f[i][1]表示第i个珠子是黄色的合法方案数,f[i][0]表示第i个珠子不是黄色的合法方案数,那么不难得到f[i][1]=f[i-1][0],f[i][0]=(K-3)*f[i-1][0]+(K-2)*f[i-1][1],最后的合法方案数就是f[R][0],由于N比较大,我们可以用二分矩阵的方法加速求解过程。

        到此,C(f)已经计算出来了,接下来的工作就是求N的逆元并计算最终的结果了。

        另外推荐一个讲解得很详细的用dp计算Burnside中C(f)的一篇文章:http://hi.baidu.com/billdu/item/62319f2554c7cac9a5275a0d

    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    #define MAXD 40010
    #define MOD 1000000007
    using namespace std;
    int N, K, isprime[MAXD], prime[MAXD], P, d[MAXD], D;
    struct Matrix
    {
        long long a[2][2];
        void init()
        {
            memset(a, 0, sizeof(a));
        }
    }unit, mat;
    Matrix multiply(Matrix &x, Matrix &y)
    {
        int i, j, k;
        Matrix z;
        z.init();
        for(i = 0; i < 2; i ++)
            for(j = 0; j < 2; j ++)
            {
                for(k = 0; k < 2; k ++)
                    z.a[i][j] += x.a[i][k] * y.a[k][j];
                z.a[i][j] %= MOD;
            }
        return z;
    }
    void prepare()
    {
        int i, j, k = 40000;
        memset(isprime, -1, sizeof(isprime));
        P = 0;
        for(i = 2; i <= k; i ++)
            if(isprime[i])
            {
                prime[P ++] = i;
                for(j = i * i; j <= k; j += i)
                    isprime[j] = 0;
            }
    }
    void divide(int n)
    {
        int i;
        D = 0;
        for(i = 1; i * i <= n; i ++)
            if(n % i == 0)
            {
                d[D ++] = i;
                if(n / i != i)
                    d[D ++] = n / i;
            }
        sort(d, d + D);
    }
    int euler(int n)
    {
        int i, x, ans;
        ans = x = n;
        for(i = 0; i < P && prime[i] * prime[i] <= n; i ++)
            if(x % prime[i] == 0)
            {
                ans = ans / prime[i] * (prime[i] - 1);
                while(x % prime[i] == 0)
                    x /= prime[i];
            }
        if(x > 1)
            ans = ans / x * (x - 1);
        return ans;
    }
    void powmod(Matrix &unit, Matrix &mat, int n)
    {
        while(n)
        {
            if(n & 1)
                unit = multiply(mat, unit);
            n >>= 1;
            mat = multiply(mat, mat);
        }
    }
    void exgcd(long long a, long long b, long long &x, long long &y)
    {
        if(b == 0)
            x = 1, y = 0;
        else
            exgcd(b, a % b, y, x), y -= x * (a / b);
    }
    void solve()
    {
        int i, j;
        long long x, y, ans = 0, n;
        divide(N);
        for(i = 0; i < D; i ++)
        {
            n = euler(N / d[i]);
            unit.init();
            unit.a[1][0] = 1;
            mat.a[0][0] = K - 3, mat.a[0][1] = K - 2, mat.a[1][0] = 1, mat.a[1][1] = 0;
            powmod(unit, mat, d[i] - 1);
            ans = (ans + ((n * K % MOD) * (K - 1) % MOD) * unit.a[0][0] % MOD) % MOD;
        }
        exgcd(N, MOD, x, y);
        x = (x % MOD + MOD) % MOD;
        ans = ans * x % MOD;
        printf("%lld\n", ans);
    }
    int main()
    {
        prepare();
        while(scanf("%d%d", &N, &K) == 2)
        {
            solve();
        }
        return 0;
    }
  • 相关阅读:
    ajax请求默认都是异步请求,怎么变为同步请求
    TP6跨域问题
    localStorage使用总结
    win10 windows management instrumentation cpu占用高解决方法
    限制性股票-股份支付
    可转债会计分类
    其他权益工具投资的交易费用计入成本
    年数总和法
    外币货币性项目汇兑差额应当计入当期损益
    chrome怎么设置点击窗口在新窗口打开
  • 原文地址:https://www.cnblogs.com/staginner/p/2506040.html
Copyright © 2011-2022 走看看