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;

}

你可能感兴趣的:(HDU)