• 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;
    }
  • 相关阅读:
    要检测两个C文件的代码的抄袭情况
    MFC简易画图
    hive中select 走与不走mapreduce
    JSP response request 中文乱码
    Hive内部自定义函数UDF
    eclipse编辑jsp没有代码提示
    Hive输出文件的间隔符
    Hadoop和HBase集群的JMX监控
    Hadoop配置项整理
    函数的递归,面向过程编程
  • 原文地址:https://www.cnblogs.com/staginner/p/2508335.html
Copyright © 2020-2023  润新知