【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

20

HINT

数据范围: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 }
View Code

 

2017-01-12 11:34:31

posted @ 2017-01-12 11:32  konjak魔芋  阅读(1440)  评论(0编辑  收藏  举报