zoukankan      html  css  js  c++  java
  • Polya计数法

    关于群、置换、置换群、Burnside定理、Polya定理的详细内容是看得组合数学课件,地址:

    http://ishare.iask.sina.com.cn/download/explain.php?fileid=15008967

    练习:

      poj2409

      题意:给一个包含s个珠子的项链,用c种颜色对其染色,问存在多少个不同的等价类。

      解:项链可以进行旋转和翻转;

      翻转:如果s是奇数,则存在s种置换,每种置换包含s/2+1个循环。

           如果s是偶数,存在s/2种以边的中点为中心轴的翻转,每种包含s/2个循环,另外还存在s/2种以点为中心的翻转,每种包含s/2+1个循环;

      旋转:旋转i个珠子得到的循环数为gcd(i, s)

        证明:设初始值为x,每次旋转i个珠子。有序列:x, x + i, x + i*2, ... , x + i*t

             假设x + i*t的时候回到原位置,即 x = (x + i*t)%n -> i*t%n = 0;

             也就是 t*i = 0(mod n)

             解这个线性同余方程。

           d = gcd(n, i);

           i'x - n'y = 0/d;

           e = x*(0/d)%n = 0;

           解的剩余系:e, e + n/d, e + n/d*2 ... e+n/d*(d-1);

           发现解的剩余系中第一个有效的解为t = n/d = n/gcd(n, i);

             所以旋转i个珠子得到的循环长度为t = n/gcd(i, n), 

           所以旋转i个珠子得到的循环数为n/t = gcd(i, n);    

    View Code
    //#pragma comment(linker,"/STACK:327680000,327680000")
    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <vector>
    #include <cstring>
    #include <algorithm>
    #include <string>
    #include <set>
    #include <functional>
    #include <numeric>
    #include <sstream>
    #include <stack>
    #include <map>
    #include <queue>
    
    #define CL(arr, val)    memset(arr, val, sizeof(arr))
    #define REP(i, n)       for((i) = 0; (i) < (n); ++(i))
    #define FOR(i, l, h)    for((i) = (l); (i) <= (h); ++(i))
    #define FORD(i, h, l)   for((i) = (h); (i) >= (l); --(i))
    #define L(x)    (x) << 1
    #define R(x)    (x) << 1 | 1
    #define MID(l, r)   (l + r) >> 1
    #define Min(x, y)   (x) < (y) ? (x) : (y)
    #define Max(x, y)   (x) < (y) ? (y) : (x)
    #define E(x)        (1 << (x))
    #define iabs(x)     (x) < 0 ? -(x) : (x)
    #define OUT(x)  printf("%I64d\n", x)
    #define Read()  freopen("data.in", "r", stdin)
    #define Write() freopen("data.out", "w", stdout);
    
    typedef long long LL;
    const double eps = 1e-8;
    const double PI = acos(-1.0);
    const int inf = ~0u>>2;
    
    using namespace std;
    
    LL gcd(LL a, LL b) {
        return b ? gcd(b, a%b) : a;
    }
    
    LL pow(LL a, LL b) {
        LL res = 1;
        while(b != 0) {
            if(b&1) res *= a;
            a = a*a;
            b >>= 1;
        }
        return res;
    }
    
    int main() {
        //freopen("data.in", "r", stdin);
    
        LL c, s, ans;
        while(cin >> c >> s) {
            if(!c && !s)    break;
            ans = 0;
            for(int i = 1; i <= s; ++i) {
                ans += pow(c, gcd(s, i));
                //printf("%lld %lld %lld %lld\n", c, gcd(s, i), pow(c, gcd(s, i)), ans);
            }
            if(s&1) {
                ans += pow(c, s/2 + 1)*s;
            } else {
                ans += pow(c, s/2 + 1)*(s/2);
                ans += pow(c, s/2)*(s/2);
            }
            cout << ans/(s*2) << endl;
        }
        return 0;
    }

       

      poj 2154

      这个题题意跟上一题类似,不过没有翻转操作。但是这个题的颜色和珠子数量都在10^9范围内,从1-10^9的范围内枚举会TLE。可以根据一些数学规律对起优化:

      上一题的思路是枚举旋转的个数i,得到循环的长度L,现在可以反过来想一下。枚举循环的长度,看存在多少i满足n/gcd(n, i) = L (n能整除L)

      设 a = n/L = gcd(n, i), i = a*t.

      则当且仅当gcd(L, t) == 1的时候满足:gcd(n, i) = gcd(a*L, a*t) = a

      问题转换成求满足gcd(L, t) == 1条件t的个数,前提是 1<= t <= L - 1

      也就是求小于L的数中有多少和L互质的数,可以容斥原理,当然也是很明显的eular函数

      可以在[1...sqrt(n)]的范围内枚举L(n%L == 0),则另外一个被N整除的数就是n/L。这样做比较慢,要跑1300+ ms

      代码:

    View Code
    //#pragma comment(linker,"/STACK:327680000,327680000")
    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <vector>
    #include <cstring>
    #include <algorithm>
    #include <string>
    #include <set>
    #include <functional>
    #include <numeric>
    #include <sstream>
    #include <stack>
    #include <map>
    #include <queue>
    
    #define CL(arr, val)    memset(arr, val, sizeof(arr))
    #define REP(i, n)       for((i) = 0; (i) < (n); ++(i))
    #define FOR(i, l, h)    for((i) = (l); (i) <= (h); ++(i))
    #define FORD(i, h, l)   for((i) = (h); (i) >= (l); --(i))
    #define L(x)    (x) << 1
    #define R(x)    (x) << 1 | 1
    #define MID(l, r)   (l + r) >> 1
    #define Min(x, y)   (x) < (y) ? (x) : (y)
    #define Max(x, y)   (x) < (y) ? (y) : (x)
    #define E(x)        (1 << (x))
    #define iabs(x)     (x) < 0 ? -(x) : (x)
    #define OUT(x)  printf("%I64d\n", x)
    #define Read()  freopen("data.in", "r", stdin)
    #define Write() freopen("data.out", "w", stdout);
    
    typedef long long LL;
    const double eps = 1e-8;
    const double PI = acos(-1.0);
    const int inf = ~0u>>2;
    
    using namespace std;
    
    const int N = 32622;
    
    int MOD, cnt;
    int prime[N];
    bool vis[N];
    
    void init() {
        LL i, j;
        CL(vis, true);
        for(i = 2; i*i <= 1e9; ++i) {
            if(vis[i]) {
                for(j = i + i; j*j <= 1e9; j += i)  vis[j] = false;
            }
        }
        cnt = 0;
        for(i = 2; i*i <= 1e9; ++i) {
            if(vis[i])  prime[cnt++] = i;
        }
    }
    
    
    LL eular(LL n) {
        int i, res = 1;
        for(i = 0; i < cnt && prime[i]*prime[i] <= n; ++i) {
            if(n%prime[i] == 0) {
                n /= prime[i]; res *= prime[i] - 1;
                while(n%prime[i] == 0) {
                    n /= prime[i]; res *= prime[i];
                }
            }
        }
        if(n > 1)   res *= n - 1;
        return res;
    }
    
    LL exp_mod(LL a, LL b) {
        LL res = 1;
        while(b != 0) {
            if(b&1) {res = (res*a)%MOD;}
            a = (a*a)%MOD;
            b >>= 1;
        }
        return res;
    }
    
    int main() {
        //Read();
    
        init();
        int T;
        int l, n, p, ans;
        scanf("%d", &T);
        while(T--) {
            scanf("%d%d", &n, &p);
            MOD = p;
            ans = 0;
            for(l = 1; l*l <= n; ++l) {
                if(n%l == 0) {
                    ans = (ans + eular(l)*exp_mod(n, n/l - 1))%MOD;
                    //printf("%lld %lld\n", eular(l), exp_mod(n, n/l - 1));
                    if(l*l == n)    continue;
                    ans = (ans + eular(n/l)*exp_mod(n, l - 1))%MOD;
                    //printf("%lld %lld\n", eular(n/l), exp_mod(n, l - 1));
                }
            }
            printf("%d\n", ans);
        }
        return 0;
    }

      可以再进一步优化,上一份代码发现时间主要用在枚举[1...sqrt(n)]范围内的L上面了。

      这里可以对n进行质因子分解,然后dfs构造出n的所有因子,时间300+ ms

    View Code
    //#pragma comment(linker,"/STACK:327680000,327680000")
    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <vector>
    #include <cstring>
    #include <algorithm>
    #include <string>
    #include <set>
    #include <functional>
    #include <numeric>
    #include <sstream>
    #include <stack>
    #include <map>
    #include <queue>
    
    #define CL(arr, val)    memset(arr, val, sizeof(arr))
    #define REP(i, n)       for((i) = 0; (i) < (n); ++(i))
    #define FOR(i, l, h)    for((i) = (l); (i) <= (h); ++(i))
    #define FORD(i, h, l)   for((i) = (h); (i) >= (l); --(i))
    #define L(x)    (x) << 1
    #define R(x)    (x) << 1 | 1
    #define MID(l, r)   (l + r) >> 1
    #define Min(x, y)   (x) < (y) ? (x) : (y)
    #define Max(x, y)   (x) < (y) ? (y) : (x)
    #define E(x)        (1 << (x))
    #define iabs(x)     (x) < 0 ? -(x) : (x)
    #define OUT(x)  printf("%I64d\n", x)
    #define Read()  freopen("data.in", "r", stdin)
    #define Write() freopen("data.out", "w", stdout);
    
    typedef long long LL;
    const double eps = 1e-8;
    const double PI = acos(-1.0);
    const int inf = ~0u>>2;
    
    using namespace std;
    
    const int N = 42622;
    
    int MOD, cnt, cntf;
    int prime[N];
    int fac[N], num[N];
    bool vis[N];
    
    int n, ans;
    
    void init() {
        LL i, j;
        CL(vis, true);
        for(i = 2; i*i <= LL(1e9+10); ++i) {
            if(vis[i]) {
                for(j = i + i; j*j <= LL(1e9+10); j += i)  vis[j] = false;
            }
        }
        cnt = 0;
        for(i = 2; i*i <= LL(1e9+10); ++i) {
            if(vis[i])  prime[cnt++] = i;
        }
    }
    
    void dd(int n) {
        int i;
        CL(fac, 0);
        CL(num, 0);
        cntf = 0;
        for(i = 0; i < cnt && prime[i]*prime[i] <= n; ++i) {
            if(n%prime[i] == 0) {
                while(n%prime[i] == 0)  {n /= prime[i]; num[cntf]++;}
                fac[cntf++] = prime[i];
            }
        }
        if(n > 1)   {num[cntf]++; fac[cntf++] = n;}
    }
    
    
    LL eular(LL n) {
        int i, res = 1;
        for(i = 0; i < cnt && prime[i]*prime[i] <= n; ++i) {
            if(n%prime[i] == 0) {
                n /= prime[i]; res *= prime[i] - 1;
                while(n%prime[i] == 0) {
                    n /= prime[i]; res *= prime[i];
                }
            }
        }
        if(n > 1)   res *= n - 1;
        return res;
    }
    
    LL exp_mod(LL a, LL b) {
        LL res = 1;
        while(b != 0) {
            if(b&1) {res = (res*a)%MOD;}
            a = (a*a)%MOD;
            b >>= 1;
        }
        return res;
    }
    
    void dfs(int i, int l) {    //构造L
        if(i == cntf) {
            ans = (ans + eular(l)*exp_mod(n, n/l - 1))%MOD;
            if(l*l != n)    ans = (ans + eular(n/l)*exp_mod(n, l - 1))%MOD;
            return ;
        }
        int tmp = 1, j;
        for(j = 0; j <= num[i]; ++j) {
            if(LL(l*tmp)*LL(l*tmp) > n) return ;
            dfs(i + 1, l*tmp);
            tmp *= fac[i];
        }
    }
    
    int main() {
        //Read();
    
        init();
        int T, p;
        scanf("%d", &T);
        while(T--) {
            scanf("%d%d", &n, &p);
            MOD = p;
            ans = 0;
            dd(n);
            dfs(0, 1);
            //cout << endl;
            printf("%d\n", ans);
        }
        return 0;
    }

    POJ 2888

      题意:还是给n(n < 10^9)个珠子,m( m <= 10)种颜色,但是现在要求这m种颜色里面有一部分的颜色是不能相邻的。

      解:因为m比较小,可以利用颜色关系构图,因为n个珠子构成一个环,想当于从第i种颜色为起点长度为n的回路有多少。《十个利用矩阵乘法解决的经典题目》中的第8题讲的就是这个,如果i,j不能相邻,则矩阵A[i][j] = 0,否则等于1。其他的做法跟上一题一样,枚举L,ans = (ans + eular(L)*A[][]^(n/L))%MOD;

      不过ans最后要除掉n,因为共有n种置换,对于除法取模,直接求逆元就可以。

      根据费马小定理,当gcd(a, p) = 1时 a^(p -1) ≡ 1 (mod p) -> a*a^(p-2) ≡ 1(mod p)。

      结果就是 ans = (ans*exp_mod(n, 9973-2))%9973;

    View Code
    //#pragma comment(linker,"/STACK:327680000,327680000")
    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <vector>
    #include <cstring>
    #include <algorithm>
    #include <string>
    #include <set>
    #include <functional>
    #include <numeric>
    #include <sstream>
    #include <stack>
    #include <map>
    #include <queue>
    
    #define CL(arr, val)    memset(arr, val, sizeof(arr))
    #define REP(i, n)       for((i) = 0; (i) < (n); ++(i))
    #define FOR(i, l, h)    for((i) = (l); (i) <= (h); ++(i))
    #define FORD(i, h, l)   for((i) = (h); (i) >= (l); --(i))
    #define L(x)    (x) << 1
    #define R(x)    (x) << 1 | 1
    #define MID(l, r)   (l + r) >> 1
    #define Min(x, y)   (x) < (y) ? (x) : (y)
    #define Max(x, y)   (x) < (y) ? (y) : (x)
    #define E(x)        (1 << (x))
    #define iabs(x)     (x) < 0 ? -(x) : (x)
    #define OUT(x)  printf("%I64d\n", x)
    #define Read()  freopen("data.in", "r", stdin)
    #define Write() freopen("data.out", "w", stdout);
    
    typedef long long LL;
    const double eps = 1e-8;
    const double PI = acos(-1.0);
    const int inf = ~0u>>2;
    
    using namespace std;
    
    const int M = 11;
    const int N = 42622;
    const int MOD = 9973;
    
    int cnt, cntf;
    int prime[N];
    int fac[N], num[N];
    bool vis[N];
    
    int n, m, ans;
    
    struct Mat {
        int mat[M][M];
    };
    
    Mat A, C;
    
    Mat operator * (Mat a, Mat b) {
        Mat c;
        memset(c.mat, 0, sizeof(c.mat));
        int i, j, k;
        for(k = 0; k < m; ++k) {
            for(i = 0; i < m; ++i) {
                if(a.mat[i][k] <= 0)  continue;   //不要小看这里的剪枝,cpu运算乘法的效率并不是想像的那么理想(加法的运算效率高于乘法,比如Strassen矩阵乘法)
                for(j = 0; j < m; ++j) {
                    if(b.mat[k][j] <= 0)    continue;    //剪枝
                    c.mat[i][j] += a.mat[i][k] * b.mat[k][j];
                    c.mat[i][j] %= MOD;
                }
            }
        }
        return c;
    }
    
    Mat operator ^ (Mat a, int k) {
        Mat c;
        int i, j;
        for(i = 0; i < m; ++i)
            for(j = 0; j < m; ++j)
                c.mat[i][j] = (i == j);    //初始化为单位矩阵
    
        for(; k; k >>= 1) {
            if(k&1) c = c*a;
            a = a*a;
        }
        return c;
    }
    
    void init() {
        LL i, j;
        CL(vis, true);
        for(i = 2; i*i <= LL(1e9+1000); ++i) {
            if(vis[i]) {
                for(j = i + i; j*j <= LL(1e9+1000); j += i)  vis[j] = false;
            }
        }
        cnt = 0;
        for(i = 2; i*i <= LL(1e9+1000); ++i) {
            if(vis[i])  prime[cnt++] = i;
        }
    }
    
    void dd(int n) {
        int i;
        CL(fac, 0);
        CL(num, 0);
        cntf = 0;
        for(i = 0; i < cnt && prime[i]*prime[i] <= n; ++i) {
            if(n%prime[i] == 0) {
                while(n%prime[i] == 0)  {n /= prime[i]; num[cntf]++;}
                fac[cntf++] = prime[i];
            }
        }
        if(n > 1)   {num[cntf]++; fac[cntf++] = n;}
    }
    
    
    LL eular(LL n) {
        int i, res = 1;
        for(i = 0; i < cnt && prime[i]*prime[i] <= n; ++i) {
            if(n%prime[i] == 0) {
                n /= prime[i]; res *= prime[i] - 1;
                while(n%prime[i] == 0) {
                    n /= prime[i]; res *= prime[i];
                }
            }
        }
        if(n > 1)   res *= n - 1;
        return res;
    }
    
    LL exp_mod(LL a, LL b) {
        LL res = 1;
        while(b != 0) {
            if(b&1) {res = (res*a)%MOD;}
            a = (a*a)%MOD;
            b >>= 1;
        }
        return res;
    }
    
    int Count(int k) {
        int i, j, res = 0;
        for(i = 0; i < m; ++i) {
            for(j = 0; j < m; ++j)  C.mat[i][j] = A.mat[i][j];
        }
        C = C^k;
        for(i = 0; i < m; ++i) {
            res = (res + C.mat[i][i])%MOD;
        }
        return res;
    }
    
    void dfs(int i, int l) {
        if(i == cntf) {
            ans = (ans + eular(l)*Count(n/l))%MOD;
            if(l*l != n)    ans = (ans + eular(n/l)*Count(l))%MOD;
            return ;
        }
        int tmp = 1, j;
        for(j = 0; j <= num[i]; ++j) {
            if(LL(l*tmp)*LL(l*tmp) > n) return ;
            dfs(i + 1, l*tmp);
            tmp *= fac[i];
        }
    }
    
    int main() {
        //Read();
    
        init();
        int i, j, x, T;
        scanf("%d", &T);
        while(T--) {
            scanf("%d%d%d", &n, &m, &x);
    
            for(i = 0; i < m; ++i) {
                for(j = 0; j < m; ++j) A.mat[i][j] = 1;
            }
    
            while(x--) {
                scanf("%d%d", &i, &j);
                i--; j--;
                A.mat[j][i] = A.mat[i][j] = 0;
            }
    
            ans = 0;
            dd(n);
            dfs(0, 1);
            ans = (ans*exp_mod(n, MOD-2))%MOD;
            printf("%d\n", ans);
        }
        return 0;
    }

        

      

  • 相关阅读:
    Extjs面板和布局初探
    XAMPP下apache部署网站,多个虚拟机(空间)配置
    安全配置织梦系统初探参考[转载]
    windows系统如何真正隐藏文件夹[转载]
    Siamese-RPN论文阅读
    线段树求和
    算法要点随记
    map使用示例
    算法准备之c++ sort使用示例
    编程要点随记
  • 原文地址:https://www.cnblogs.com/vongang/p/2873780.html
Copyright © 2011-2022 走看看