题目分析
可以看论文,不过还是讲讲本蒟蒻的理解吧。
这题很让人苦恼的一个地方在于,所谓循环节是点的循环,而我们要求的方案是落在边上的。于是我们先考虑点的循环如何转化为边的循环。
对于一个长度为 L L L的循环节,可以将其看作一个环,环上的边是有向边,来代表点之间的置换。以该循环节中两点为端点的边的循环节个数就是 ⌊ L 2 ⌋ \lfloor \frac{L}{2} \rfloor ⌊2L⌋个。
如图(为什么这么辣眼睛QAQ,总之同种颜色的边属于同一个循环节,个数为3)
如果边的两个端点是两个不同的,大小分别为 L 1 L_1 L1和 L 2 L_2 L2循环节,可以假装连接其中的两端点的线在不停转啊转…咳咳,总之,循环节个数为 g c d ( L 1 , L 2 ) gcd(L_1,L_2) gcd(L1,L2)。(下图循环节个数为2,对应红色和蓝色)
所以我们可以暴力拆分 n n n个点被分为的循环节们长什么样。那么已经知道了循环节的组合,这样的置换有多少个呢?
假设当前拆出的点的循环节为 L 1 , L 2 , L 3 . . . L m L_1,L_2,L_3...L_m L1,L2,L3...Lm,那么将不同编号的点往这些 L L L里塞,共计 n ! L 1 ! L 2 ! . . . L m ! \frac{n!}{L_1!L_2!...L_m!} L1!L2!...Lm!n!种方式。对于每个循环,确定第一点后,后面的点排列不同,对应不同置换,所以还要乘以 ( L 1 − 1 ) ! ( L 2 − 1 ) ! . . . ( L m − 1 ) ! (L_1-1)!(L_2-1)!...(L_m-1)! (L1−1)!(L2−1)!...(Lm−1)!
如果有一些 L L L相同,那么就会有方案重复,所以还要除以重复方案的排列,得到的答案就是:
n ! L 1 L 2 . . . L m k 1 ! k 2 ! . . . k t ! \frac{n!}{L_1L_2...L_m k_1!k_2!...k_t!} L1L2...Lmk1!k2!...kt!n!
其中 t t t代表共有 t t t种不同的 L L L值,每种有 k i k_i ki个。
所以循环节的贡献可以靠dfs拆分搞出来,而 ∣ G ∣ = n ! |G|=n! ∣G∣=n!。带入Polya定理的公式,循环节相同的贡献统一计算即可。
代码
技巧:尽可能地预处理一切。
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define ri register int
LL n,m,p,ans,gd[60][60],fac[60],L[60],K[60];
LL gcd(LL a,LL b) {return (!b)?a:gcd(b,a%b);}
LL ksm(LL x,LL y) {
LL re=1;
for(;y;y>>=1,x=x*x%p) if(y&1) re=re*x%p;
return re;
}
void work(int tot) {
LL k1=1,k2=0;
for(ri i=1;i<=n;++i) K[i]=0;
for(ri i=1;i<=tot;++i) ++K[L[i]];
for(ri i=1;i<=n;++i) k1=k1*fac[K[i]]%p;
for(ri i=1;i<=tot;++i) k1=k1*L[i]%p;
for(ri i=1;i<=tot;++i) {
k2+=L[i]/2;
for(ri j=i+1;j<=tot;++j) k2+=gd[L[j]][L[i]];
}
ans=(ans+ksm(m,k2)*fac[n]%p*ksm(k1,p-2)%p)%p;
}
void dfs(int x,int tot,int las) {
if(x==n) {work(tot);return;}
for(ri i=1;i<=las&&x+i<=n;++i) L[tot+1]=i,dfs(x+i,tot+1,i);
}
int main()
{
scanf("%lld%lld%lld",&n,&m,&p);
for(int i=1;i<=n;++i)
for(int j=i;j<=n;++j) gd[i][j]=gcd(i,j);
fac[0]=1;for(int i=1;i<=n;++i) fac[i]=fac[i-1]*i%p;
dfs(0,0,n),printf("%lld\n",ans*ksm(fac[n],p-2)%p);
return 0;
}