HDU 2481 Toy

HDU_2481

    由于需要处理循环同构的问题,明显需要用burnside引理,于是关键问题就在于对于一个N个约数R(表示一共有R个循环节),连续R个珠子一共可以形成多少种合法的方案。

    这个让我联想到了之前做过的“FJOI 2007 轮状病毒”这个题目,不难发现连续R个珠子所能形成的合法方案,和R个珠子形成的轮状病毒的数目是一致的,因此我们完全可以把那个题目的递推式拿过来做dp的递推式,我推的递推公式在轮状病毒这个题目的解题报告里面:http://www.cnblogs.com/staginner/archive/2012/03/27/2420347.html,用这个递推式去dp的话需要构造4*4的矩阵,然后用二分矩阵的方法求解,网上还可以搜到其他的递推过程。

    这样dp的环节就可以搞定了,其他的按burnside引理的套路来就可以了。最后由于模的那个数M不一定和N互质,所以不能像MOD是素数那样去求N的逆元再相乘了。借鉴了一下网上看到的方法,令MOD=M*N,算出结果之后直接除以N就可以了。

    此外,即便算法想到了也一定不能忽视了代码上的常数优化,我一开始交上去便TLE了,后来翻翻别人的解题报告发现除了dp的部分不太一样就剩下常数优化了,做了几处优化之后便TLE->2500ms->500ms,效率有了质的飞跃……所以以后还是要注重代码的常数优化呀。

    常数优化的地方已经在代码里加以说明了。

#include<stdio.h>
#include<string.h>
#define MAXD 40010
int N, isprime[MAXD], prime[MAXD], P, p[MAXD], pn;
long long M;
struct Matrix
{
    long long a[4][4];
    void init()
    {
        memset(a, 0, sizeof(a));
    }
}unit, mat[40];
long long mul(long long x, long long y)
{
    long long ans = 0;
    x %= M;
    while(y)
    {
        /*
        if(y & 1)
            ans = (ans + x) % M;
        y >>= 1;
        x = (x + x) % M;
        用条件判断和减法取代模运算,常数优化
        */
        if((y & 1) && (ans += x) >= M)
            ans -= M;
        y >>= 1;
        if((x <<= 1) >= M)
            x -= M;
    }
    return ans;
}
Matrix multiply(Matrix &x, Matrix &y)
{
    int i, j, k;
    Matrix z;
    z.init();
    for(i = 0; i < 4; i ++)
        for(k = 0; k < 4; k ++)
            if(x.a[i][k])
            {
                for(j = 0; j < 4; j ++)
                    if(y.a[k][j])
                        z.a[i][j] = (z.a[i][j] + mul(x.a[i][k], y.a[k][j])) % M;
            }
    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;
        }
}
int euler(int n)
{
    int i, ans = n;
    for(i = 0; i < pn; i ++)
        if(n % p[i] == 0)
            ans = ans / p[i] * (p[i] - 1);
    return ans;
}
void divide(int n)
{
    int i, j;
    pn = 0;
    for(i = 0; i < P && prime[i] * prime[i] <= n; i ++)
        if(n % prime[i] == 0)
        {
            p[pn ++] = prime[i];
            while(n % prime[i] == 0)
                n /= prime[i];
        }
    if(n > 1)
        p[pn ++] = n;
}
void initmat() //预先处理出递推关系矩阵的2^x幂,常数优化
{
    int i;
    mat[0].init();
    mat[0].a[0][0] = 2, mat[0].a[0][2] = 1, mat[0].a[0][3] = M - 1;
    mat[0].a[1][0] = mat[0].a[1][1] = mat[0].a[1][2] = 1;
    mat[0].a[2][0] = 1, mat[0].a[2][2] = 2, mat[0].a[2][3] = M - 1;
    mat[0].a[3][2] = 1;
    for(i = 1; i < 32; i ++)
        mat[i] = multiply(mat[i - 1], mat[i - 1]);
}
void powmod(Matrix &unit, int n)
{
    int i;
    for(i = 0; n; i ++, n >>= 1)
        if(n & 1)
            unit = multiply(mat[i], unit);
}
void dfs(int cur, int R, int x, long long &ans)
{
    int i, cnt = 0, t = 1;
    if(cur == pn)
    {
        int n = euler(N / R);
        if(R == 1)
            ans = (ans + n) % M;
        else
        {
            unit.init();
            unit.a[0][0] = 3, unit.a[1][0] = 2, unit.a[2][0] = 3, unit.a[3][0] = 1;
            powmod(unit, R - 2);
            ans = (ans + mul(n, unit.a[0][0] + unit.a[1][0])) % M;
        }
        return ;
    }
    while(x % p[cur] == 0)
        ++ cnt, x /= p[cur];
    for(i = 0; i <= cnt; i ++)
    {
        dfs(cur + 1, R * t, x, ans);
        t *= p[cur];
    }
}
void solve()
{
    int i;
    long long ans, x, y, n;
    ans = 0;
    divide(N);
    initmat();
    dfs(0, 1, N, ans);
    printf("%I64d\n", ans / N);
}
int main()
{
    prepare();
    while(scanf("%d%I64d", &N, &M) == 2)
    {
        M *= N;
        solve();
    }
    return 0;
}
原文地址:https://www.cnblogs.com/staginner/p/2508335.html