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;
}
原文地址:https://www.cnblogs.com/staginner/p/2506040.html