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;
}

原文地址:https://www.cnblogs.com/zbr162/p/11822574.html