有限制的Burnside , 矩阵幂

poj  2888

给一串有n(1<=10^9 && gcd(n,9973))颗珠子的项链染色,但是有一些颜色限制,已知某些{ci,cj}(i可能等于j)颜色不能出现在相连的珠子中。项链在平面内转动(不能翻转)得到的为等价方案,求不同的染色方案数mod 9973。


由于项链只能绕中心转动,而不能翻转,所以有一个很好的性质:项链转 2*PI*k/n个角度的置换共有 gcd(k,n)个长度均为len=n/gcd(k,n)的循环节,而且可以发现,若gcd(k,n)>1,则从某一个编号的项链开始顺时针(或逆时针)相邻的len个珠子两两处于不同的循环节中。这样k置换(1<=k<=n)的固定构型数等价于:
给长度为gcd(k,n)的序列染色(没写错,确实是序列),其中所有相邻编号(beg和end为相邻的)为合法对(若color i 和 color j 能够分别出现在相邻的两珠子上,那么说(i,j)是合法对)的染色方案。

假设一个长度为 L 的序列的合法表示为 a1,a2,……,aL 那么(a1,a2)(a2,a3),……,(a(L-1),aL),(aL,a1)必定都为1 ,所以令 g[ai,aj]=1表示(ai,aj)为合法对,g[ai,aj]=0表示(ai,aj)为非法对。那么以a1开头的合法序列为k个那么必定存在且仅存在k个不同的序列{b1,b2,b3,b4,……,b(L-1)}满足 g[a1,b1]*g[b1,b2]*……g[b(L-2),b(L-1)]*g[b(L-1),a1]=1; 这样很显然就是矩阵g[ai,aj]的L次幂中g[a1,a1]的的值。

证明polya定理的时候,m^c(ai)这个项是这样得出的:有c(ai)个循环,因为只有每个循环内部的所有点都是同一种颜色,该状态才不会因为某种置换变成其它状态,所以c(ai)个循环,每个循环都选择一种颜色,总的方案数就是m^c(ai),现在有禁区,那么用dp就应该可以求出总的方案数了,因为只有旋转这一种置换方式,所以各个循环中的珠子都是像循环节一样出现的,用f[i][j]表示i到j的路径数(走一步时),那么走k次,就相当于f^k,最后就求出得到的矩阵的合法解的个数就是了.有一种更好的方法是求出f^(k+1),然后求出最后得到的矩阵g的Σg[i][i]就可以了.
然后就是求出有多少个数i,使得gcd(n,i)=x(详细的看上一篇总结).这个用欧拉函数就可以了(虽然我不是这样做的).
最后得到了总和s,还要求出(s/n)mod9973,因为9973是质数,由费马小定理可知:n^(9973-1)mod 9973=1
所以(s/n)mod9973=(s/n)*n^(9973-1)mod9973=s*n^(9973)mod9973
二分快速幂即可.

可惜常数比较大,最坏情况下每组数据的时间复杂度应该是O(m^3logn*2^9),

#include<iostream>
#include<cstring>
#include<queue>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define N 1000000000
#define inf 1<<29
#define MOD 9973
#define LL long long
using namespace std;
struct Matrix{
    int m[15][15];
}init;
int s,c,k;
bool flag[40000]={0};
int prime[40000],cnt=0;
Matrix operator*(Matrix m1,Matrix m2){
    Matrix ans;
    for(int i=0;i<c;i++)
        for(int j=0;j<c;j++){
            ans.m[i][j]=0;
            for(int k=0;k<c;k++)
                ans.m[i][j]=(ans.m[i][j]+m1.m[i][k]*m2.m[k][j])%MOD;
        }
    return ans;
}
Matrix operator^(Matrix m1,int b){
    Matrix ans;
    for(int i=0;i<c;i++)
        for(int j=0;j<c;j++)
            ans.m[i][j]=(i==j);
    while(b){
        if(b&1)
            ans=ans*m1;
        m1=m1*m1;
        b>>=1;
    }
    return ans;
}
void Prime(){
    for(int i=2;i<=sqrt(N+1.0);i++){
        if(flag[i]) continue;
        prime[cnt++]=i;
        for(int j=2;j*i<=sqrt(N+1.0);j++)
            flag[i*j]=true;
    }
}
int PowMod(int a,int b){
    a%=MOD;
    int ret=1;
    while(b){
        if(b&1)  ret=(ret*a)%MOD;
        a=(a*a)%MOD;
        b>>=1;
    }
    return ret;
}
int Eular(int n){
    int ret=1;
    for(int i=0;i<cnt&&prime[i]*prime[i]<=n;i++){
        if(n%prime[i]==0){
            n/=prime[i];ret*=prime[i]-1;
            while(n%prime[i]==0){n/=prime[i];ret=(ret*prime[i])%MOD;}
        }
    }
    if(n>1) ret*=n-1;
    return ret%MOD;
}
void debug(Matrix t){
    for(int i=0;i<c;i++){
        for(int j=0;j<c-1;j++)
            printf("%d ",t.m[i][j]);
        printf("%d\n",t.m[i][c-1]);
    }
}
int slove(int n){
    Matrix temp=init^n;
    int ans=0;
    for(int i=0;i<c;i++)
        ans=(ans+temp.m[i][i])%MOD;
    return ans;
}
int Polya(){
    int i,ans=0;
    for(i=1;i*i<s;i++)
        if(s%i==0)
           ans=(ans+Eular(i)*slove(s/i)+Eular(s/i)*slove(i))%MOD;
    if(i*i==s) ans=(ans+Eular(i)*slove(i))%MOD;
    return (ans*PowMod(s%MOD,MOD-2))%MOD;
}
int main(){
    int t;
    Prime();
    scanf("%d",&t);
    while(t--){
        scanf("%d%d%d",&s,&c,&k);
        for(int i=0;i<c;i++)
            for(int j=0;j<c;j++)
                init.m[i][j]=1;
        while(k--){
            int a,b;
            scanf("%d%d",&a,&b);
            a--;b--;
            init.m[a][b]=init.m[b][a]=0;
        }
        printf("%d\n",Polya());
    }
    return 0;
}


你可能感兴趣的:(有限制的Burnside , 矩阵幂)