zoukankan      html  css  js  c++  java
  • POJ2888 Magic Bracelet [矩阵快速幂+Burnside+欧拉函数]

    Magic BraceletMagic Bracelet


    Descriptionmathcal{Description}
    给一条长度为nn的项链,有mm种颜色,另有kk条限制,每条限制为不允许x,yx,y颜色连在一起。
    求有多少种本质不同的染色方式,本质不同的两种染色方式旋转不能互相得到 .


    正解部分
    对于 置换: “旋转 kk 个单位”
    在没有颜色限制的情况下, 染色方案数为 Mgcd(N,k)M^{gcd(N,k)},

    在有颜色限制的情况下,
    ,color{red}{不同循环节的元素相互之间是挨着的,}

    • 要保证第 ii 个与第 i+1,i1i+1,i-1个循环节颜色不同 .
    • 由于是环, 还需保证第11个与第NN个循环节颜色不同 .

    在以上限制下, 如何求解方案数呢?
    F[i,j]F[i,j] 表示前 ii 个元素, 第 ii 个元素颜色为 jj 的方案数, ld[i,j]=1/0ld[i,j]=1/0 表示 iijj 是否可以放到一起.
    考虑到首尾相连, 枚举第11个循环节选的 colcol 颜色, 进行 dpdp,

    {F[1,col]=1, F[i,j]=k=1Mld[j,k]F[i1,k]         i[ 2,gcd(N,k) ), F[gcd(N,k),j]=k=1,k != colMld[j,k]F[gcd(N,k)1,k]egin{cases}F[1,col]=1,\ \ F[i,j]=sum_{k=1}^{M}ld[j,k]*F[i-1,k] i∈[ 2,gcd(N,k) ), \ \ F[gcd(N,k),j]=sum_{k=1,k != col}^{M}ld[j,k]*F[gcd(N,k)-1,k] end{cases}

    于是 i=1MF[gcd(N,k),i]sum_{i=1}^{M}F[gcd(N,k),i] 即为该置换方案数 , 暂存到 F[gcd(N,k),0]F[gcd(N,k),0] 里.

    使color{blue}{中间的式子可以使用矩阵快速幂优化}


    再看我们要求的式子
    L=1Ni=1NF[gcd(N,k),0]L=frac{1}{N}sum_{i=1}^{N}F[gcd(N,k),0]
    变为
    L=1NdNF[d,0]φ(Nd)L=frac{1}{N}sum_{d|N}F[d,0]*varphi(frac{N}{d})

    至于为什么请点击这里看拓展2

    时间复杂度O(M4NlogN)O(M^4*sqrt{N}*logN).

    如果你耐心的看到了这里, 那么不幸的告诉你, 这个方法会 TLE

    建立一个 MMM*M 的矩阵, Ci,j=1C_{i,j}=1 表示 ii 可以走到 jj,
    计算出这个矩阵 Ngcd(N,k)frac{N}{gcd(N,k)} 次方, 则对角线的值加起来即为该置换的方案总数 .


    注意 求解 phiphi的时候不能 模.

    调了一个晚上, 竟然是phiphi忘记加特判了!!


    #include<cmath>
    #include<cstdio>
    #include<cctype>
    #include<cstring>
    #define reg register
    
    const int mod = 9973;
    
    int read(){
            char c;
            int s = 0, flag = 1;
            while((c=getchar()) && !isdigit(c))
                    if(c == '-'){ flag = -1, c = getchar(); break ; }
            while(isdigit(c)) s = s*10 + c-'0', c = getchar();
            return s * flag;
    }
    
    int N;
    int M;
    int K;
    
    struct Matrix{ int C[12][12]; Matrix(){ memset(C, 0, sizeof C); } } A;
    
    Matrix Modify(Matrix a, Matrix b){ // 
            Matrix s;
            for(reg int i = 1; i <= M; i ++)
                    for(reg int j = 1; j <= M; j ++){ 
                            int &t = s.C[i][j];
                            for(reg int k = 1; k <= M; k ++)
                                    t = (t+a.C[i][k]*b.C[k][j]) % mod;
                    }
            return s;
    }
    
    Matrix KSM(Matrix a, int b){ //
            Matrix s;
            for(reg int i = 1; i <= M; i ++) s.C[i][i] = 1;
            while(b){
                    if(b & 1) s = Modify(s, a);
                    a = Modify(a, a);
                    b >>= 1;
            }
            return s;
    }
    
    int phi(int x){ //
            int s = x;
            for(reg int i = 2; i*i <= x; i ++){
                    if(x % i) continue ;
                    s = s/i*(i-1);
                    while(x%i == 0) x /= i;
            }
            if(x > 1) s = s/x*(x-1);
            return s%mod;
    }
    
    int KSM_2(int a, int b){ //
            int s = 1;
            a %= mod;
            while(b){
                    if(b & 1) s = (s*a) % mod;
                    a = (a*a) % mod;
                    b >>= 1;
            }
            return s;
    }
    
    int gcd(int a, int b){ return !b?a:gcd(b, a%b); } //
    
    int Calc(int k){
            int tmp = 0;
            Matrix B = KSM(A, k);
            for(reg int i = 1; i <= M; i ++) tmp += B.C[i][i], tmp %= mod; 
            tmp = (phi(N/k)*tmp) % mod;
            return tmp;
    }
    
    void Work(){
            N = read(), M = read(), K = read();
            for(reg int i = 1; i <= M; i ++)
                    for(reg int j = 1; j <= M; j ++) A.C[i][j] = 1;
            for(reg int i = 1; i <= K; i ++){
                    int a = read(), b = read();
                    A.C[a][b] = A.C[b][a] = 0;
            }
            int Ans = 0;
            for(reg int k = 1; k*k <= N; k ++)
                    if(N % k == 0){
                            Ans = (Ans + Calc(k)) % mod; 
                            int k2 = N/k;
                            if(k != k2) Ans = (Ans + Calc(k2)) % mod;
                    }
            Ans = 1ll*Ans * KSM_2(N, mod-2) % mod;
            printf("%d
    ", Ans);
    }
    
    int main(){
            int T = read();
            while(T --) Work();
            return 0;
    }
    
    
  • 相关阅读:
    JavaScript 随机产生十个整数,放入数组中,对这个数组进行降序排序,并获取到这个数组的最大值和最小值
    JavaScript输出换行
    JavaScript超时调用、间歇调用
    JavaScript内置对象
    JavaScript事件列表
    JavaScript 中 for-in和 for-of 的区别
    JavaScript break指定标签打破多层循环示例
    初识JavaScript
    HTML + CSS CSS设置背景图片后图片没有铺满屏幕等
    设计模式之工厂模式
  • 原文地址:https://www.cnblogs.com/zbr162/p/11822574.html
Copyright © 2011-2022 走看看