【BZOJ 1478】 1478: Sgu282 Isomorphism (置换、burnside引理)
1478: Sgu282 Isomorphism
Description
给 定一个N 个结点的无向完全图( 任意两个结点之间有一条边), 现在你可以用 M 种颜色对这个图的每条边进行染色,每条边必须染一种颜色。 若两个已染色的图,其中一个图可以通过结点重新编号而与另一个图完全相同, 就称这两个染色方案相同。 现在问你有多少种本质不同的染色方法,输出结果 mod P。P 是一个大于N 的质数。Input
仅一行包含三个数,N、M、P。Output
仅一行,为染色方法数 mod P 的结果。Sample Input
3 4 97
Sample Output
20HINT
数据范围:1≤N≤53,1≤M≤1000,N
【分析】
关于这题,这文档讲得很清楚:http://wenku.baidu.com/view/fee9e9b9bceb19e8b8f6ba7a.html?from=search###
这题想起来挺难的。
首先它是对点的置换,但是是边染上了颜色,就是说实际上是边的置换。所以我们要看一下点置换和边置换之间的关系。
假定一个点置换,把它表示为循环,比如是(a1,a2,....)(b1,b2...)(c1,c2...)...
1、对于不在一个循环里面的点:
比如a1,b1, 那么会有边循环((a1,b1),(a2,b2)...) 设a循环的循环节是l1,b循环的循环节是l2,那么形成的边循环的循环节显然是LCM(l1,l2)。
一共有l1*l2个点对,每个点对都在一个循环节为LCM(l1,l2)的循环上,所以一共有l1*l2/LCM(l1,l2)=GCD(l1,l2)个循环节,所以C(f)=m^GCD(l1,l2)。(回到burnside引理,C为置换之后仍为本身的数目,就是说要循环节里的每条边都一样的颜色)
2、对于在一个循环里面的点:
比如a1、a2。设这个a循环的循环节为l1。
如果l1是奇数,那么循环长度为l1,一共有C(l1,2)个点对,所以是(l1-1)/2个循环节,所以C(f)=m^((l1-1)/2)。
如果l1是偶数,除了上面这种情况之外,还有一种的循环节是l1/2(就是两个点刚好相隔半个周期,而边是双向的),所以一共有(C(l1,2)-l1/2)/l1+1=l1/2个点对。
整理一下:
所以代码很简单,只要枚举n的拆分,然后计算不动点就好了。这里有用到逆元,p是质数可以用费马小定理。
分母上面先乘完再求逆元,我就是一边乘一边逆元就超时了。。。ORZ。。。
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 using namespace std; 7 #define Maxn 60 8 #define LL long long 9 10 int n,m; 11 LL pw[Maxn],N,p,ans; 12 13 LL qpow(LL a,LL b) 14 { 15 LL ans=1; 16 while(b) 17 { 18 if(b&1) ans=(ans*a)%p; 19 a=(a*a)%p; 20 b>>=1; 21 } 22 return ans; 23 } 24 25 LL l[Maxn]; 26 27 int gcd(int a,int b) 28 { 29 if(b==0) return a; 30 return gcd(b,a%b); 31 } 32 33 void get_ans(int x) 34 { 35 int c=0; 36 for(int i=1;i<=x;i++) c+=l[i]/2; 37 for(int i=1;i<=x;i++) 38 for(int j=i+1;j<=x;j++) c+=gcd(l[i],l[j]); 39 LL now=1; 40 for(int i=1;i<=x;i++) now=(now*l[i])%p; 41 int cnt=1; 42 for(int i=2;i<=x;i++) 43 { 44 if(l[i]!=l[i-1]) 45 { 46 now=(now*pw[cnt])%p; 47 cnt=0; 48 } 49 cnt++; 50 } 51 now=(now*pw[cnt])%p; 52 now=(N*qpow(now,p-2))%p; 53 ans=(ans+now*qpow(m,c))%p; 54 // printf("%d\n",ans); 55 } 56 57 void ffind(int x,int st,int h) 58 { 59 if(h==0) 60 { 61 get_ans(x-1); 62 } 63 if(h<st) return; 64 for(int i=st;i<=h;i++) 65 { 66 l[x]=i; 67 ffind(x+1,i,h-i); 68 } 69 } 70 71 int main() 72 { 73 scanf("%d%d%lld",&n,&m,&p); 74 N=1; 75 for(int i=1;i<=n;i++) N=(N*i)%p; 76 pw[0]=1; 77 for(int i=1;i<=n;i++) pw[i]=(pw[i-1]*i)%p; 78 ans=0; 79 ffind(1,1,n); 80 printf("%lld\n",(ans*qpow(N,p-2))%p); 81 return 0; 82 }
2017-01-12 11:34:31