求:用C种颜色染N阶完全图的不同染色方案(mod P),P为素数(两个染色方案是相同的当且仅当顶点编号重排后各边的颜色相同),
其中1<=N<=53,1<=C<=1000;

很明显这个群有N!个置换(顶点的排列),一个置换可表示为 (a1,a2,……,am)(b1,b2,……,bn)(c1,c2,……,ck) ……循环节的形式,其中m+n+k=N。以下证明:
 
1: 在一个置换下,关联的顶点都在某个L长的循环节的边,可分成一共floor(L/2)个等价类。
2:在一个置换下,关联的顶点分别在两个长度为L1,L2的循环节内的边,可分成一共gcd(L1,L2)个等价类。

证明1: 在下面的证明中[x] 表示 (x mod L)

由于边{a[i],a([i]+[k])},{a[j],a([j]+[k])} ([k]>=1 && k<=L-1)在同一个等价类 ,而且 {a[i],a([i]+[k])} 与 {a[i],a([i]+[L-k])}在同一个等价类,所以
[k]={1,2,3,……,floor(L/2)} 和 [k]={L-1,L-2,……,ceil(L/2)}分别对应同一个等价类。所以1得证。

证明2:

若v1属于{a1,a2,……,am},v2属于{b1,b2,……,bn},则{v1,v2}的等价边为 {v1+[t],v2+[t]},其中,在t从 1开始增加的过程中,首次出现v1==v1+[t] && v2==v2+[t] 是在 t=lcm(m,n)的时候,所以每一条边共有lcm(m,n)条等价边,所以共有 m*n/lcm(m,n)个等价类 即为 gcd(m,n);

如果一个置换可以表示为 长度分别为 L1,L2,……,Lk(非降序排列)的k个循环节,那么在这个置换下边共有 n=Sum(floor(Li/2) ) + Sum(gcd(Li,Lj))个置换群;
而 k1*L1+k2*L2+k3*L3+……km*Lm=N(其中 Li != Lj for i != j) 的置换共有
 (N! *( (L1-1)! )^k1 *( (L2-1)!)^k2 *……* ((Lm-1)!)^km  )    /   (  ( (L1!)^k1 * (L2!)^k2 * (L3!)^k3*……(Lm!)^km )*  (k1! * k2! * k3!*……*km!)  )
即为 tot = N!/( (L1^k1)*(L2^k2)*(L3^k3)*……(Lm^km) * (k1! * k2! * k3! * …… *km!));
所以可以表示成这个形式(k1*L1+k2*L2+k3*L3+……+km*Lm)的固定构型个数为 ans= n* pow (C,tot)  个。

  1 #include <iostream>
 2 #include <cstring>
 3 #include <cstdio>
 4 using namespace std;
 5 
 6 const int max_n=60;
 7 typedef long long LL;
 8 
 9 int N,C,P;
10 
11 LL facv[max_n],inv[max_n];
12 
13 LL mypow(LL a,LL b){
14     LL res=1,radix=a%P;
15     while(b){
16         if(b&1){
17             res*=radix;    res%=P;
18         }    
19         radix*=radix;    radix%=P;
20         b>>=1;
21     }
22     return res;
23 }
24 
25 LL cal_inv(int i){
26     return mypow(i,P-2);
27 }
28 
29 int gcd(int i,int j){
30     if(j==0)    return i;
31     return gcd(j,i%j);
32 }
33 void ini(){
34     facv[1]=1;
35     inv[1]=1;
36     for(int i=2;i<=N;i++){
37         inv[i]=cal_inv(i);
38         facv[i]=facv[i-1]*inv[i];    facv[i]%=P;
39     }
40 }
41 
42 int factor[max_n][2];    LL ans;
43 
44 void search(int sum,int top){
45     if(sum==N){
46         LL n=1;    
47         for(int i=1;i<=top;i++){
48             int len=factor[i][0],cnt=factor[i][1];
49             n*=mypow(inv[len],cnt); n%=P;            
50             n*=facv[cnt];    n%=P;
51         }
52         LL tot=0;
53         for(int i=1;i<=top;i++){
54 
55             int len=factor[i][0],cnt=factor[i][1];
56             tot+=cnt*(len/2);            //the same circle
57             tot+=cnt*(cnt-1)/2*len;        //two different circle
58 
59             for(int j=i+1;j<=top;j++){
60                 int len_j=factor[j][0],    cnt_j=factor[j][1];
61                 tot+=(cnt*cnt_j*gcd(len_j,len));
62             }
63         }
64         ans+=(n*mypow(C,tot))%P;    ans%=P;
65         return;
66     }
67     if(sum+factor[top][0]>=N){
68         return;
69     }
70     for(int i=factor[top][0]+1;i<=N-sum;i++){
71         for(int j=1;sum+i*j<=N;j++){
72             factor[top+1][0]=i;    factor[top+1][1]=j;
73             search(sum+i*j,top+1);
74         }
75     }
76 }
77 int main()
78 {
79     while(scanf("%d%d%d",&N,&C,&P)!=EOF){
80         ini();
81         factor[0][0]=0;
82         ans=0;
83         search(0,0);
84         printf("%lld\n",ans);
85     }
86     return 0;
87 }
88