题目分析
可以看论文,不过还是讲讲本蒟蒻的理解吧。
这题很让人苦恼的一个地方在于,所谓循环节是点的循环,而我们要求的方案是落在边上的。于是我们先考虑点的循环如何转化为边的循环。
对于一个长度为LLL的循环节,可以将其看作一个环,环上的边是有向边,来代表点之间的置换。以该循环节中两点为端点的边的循环节个数就是⌊L2⌋\lfloor \frac{L}{2} \rfloor⌊2L⌋个。
如图(为什么这么辣眼睛QAQ,总之同种颜色的边属于同一个循环节,个数为3)
如果边的两个端点是两个不同的,大小分别为L1L_1L1和L2L_2L2循环节,可以假装连接其中的两端点的线在不停转啊转…咳咳,总之,循环节个数为gcd(L1,L2)gcd(L_1,L_2)gcd(L1,L2)。(下图循环节个数为2,对应红色和蓝色)
所以我们可以暴力拆分nnn个点被分为的循环节们长什么样。那么已经知道了循环节的组合,这样的置换有多少个呢?
假设当前拆出的点的循环节为L1,L2,L3...LmL_1,L_2,L_3...L_mL1,L2,L3...Lm,那么将不同编号的点往这些LLL里塞,共计n!L1!L2!...Lm!\frac{n!}{L_1!L_2!...L_m!}L1!L2!...Lm!n!种方式。对于每个循环,确定第一点后,后面的点排列不同,对应不同置换,所以还要乘以(L1−1)!(L2−1)!...(Lm−1)!(L_1-1)!(L_2-1)!...(L_m-1)!(L1−1)!(L2−1)!...(Lm−1)!
如果有一些LLL相同,那么就会有方案重复,所以还要除以重复方案的排列,得到的答案就是:
n!L1L2...Lmk1!k2!...kt!\frac{n!}{L_1L_2...L_m k_1!k_2!...k_t!}L1L2...Lmk1!k2!...kt!n!
其中ttt代表共有ttt种不同的LLL值,每种有kik_iki个。
所以循环节的贡献可以靠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;
}