
本文共 2331 字,大约阅读时间需要 7 分钟。
题目链接
思路
显然有 N! N ! 个边的置换,枚举边的置换肯定不可行。
因此,我们来考虑点置换和边置换的关系。
首先,考虑一条边 (x,y) ( x , y ) 。
如果 x x 和 在点置换中的同一循环节中,设这个循环节长度为 L L 。
若 为奇数,则 (x,y) ( x , y ) 所处边置换中循环节长度为 L L ,一共有 条边,所以循环节的个数为 C2LL=L−12 C L 2 L = L − 1 2 个。
若 L L 为偶数,则 所处边置换,出来上述长度为 L L 的情况,还有一种情况:
如果 在循环节中正好相距 L2 L 2 ,那么循环节长度为 L2 L 2 这样的循环节一共有 L2 L 2 个,循环节的个数为 C2L−L2L+1=L2 C L 2 − L 2 L + 1 = L 2 个循环节。
再来考虑 x,y x , y 不在一个循环节的情况,设循环节长度分别为 L1,L2 L 1 , L 2 。
显然, (x,y) ( x , y ) 所在置换的循环节长度为 lcm(L1,L2) l c m ( L 1 , L 2 ) ,这样的循环节个数为 L1L2lcm(L1,L2)=gcd(L1,L2) L 1 L 2 l c m ( L 1 , L 2 ) = g c d ( L 1 , L 2 ) 个。
若一个点置换的循环节长度分别为 L1,L2,⋯,LK L 1 , L 2 , ⋯ , L K ,那么边置换的循环节长度为:
接下来我们求满足循环节长度为 L1,L2,⋯,LK L 1 , L 2 , ⋯ , L K 的边置换个数。
这就相当于将 1⋯N 1 ⋯ N 放入大小为 L1,L2,⋯,LK L 1 , L 2 , ⋯ , L K 的环中,求总方案数。
显然,如 L L 不重复,那么总方案数为 ,这里不给出证明了。
若 L L 有重复,假设 中有 t t 种不同的值,第 种值的个数为 ki k i ,那么总方案数(也就是满足条件的边置换个数)为:
最终的答案为:
代码
#include <cstdio>const int maxn=53;int fac[maxn+1],l[maxn+1],n,m,p,ans;int gcd(int x,int y){ return y?gcd(y,x%y):x;}inline int quickpow(int a,int b,int mo){ int res=1; while(b) { if(b&1) { res=1ll*res*a%mo; } a=1ll*a*a%mo; b>>=1; } return res;}inline int getans(int x){ int c=0,s=1,cnt=0; for(register int i=1; i<=x; ++i) { c+=l[i]/2; for(register int j=i+1; j<=x; ++j) { c+=gcd(l[i],l[j]); } s=(1ll*s*l[i])%p; if(l[i]!=l[i-1]) { s=(1ll*s*fac[cnt])%p; cnt=0; } ++cnt; } s=(1ll*s*fac[cnt])%p; s=(1ll*fac[n]*quickpow(s,p-2,p))%p; ans=(1ll*ans+((1ll*s*quickpow(m,c,p))%p))%p; return 0;}int search(int now,int lo,int hi){ if(!hi) { getans(now-1); } if(hi<lo) { return 0; } for(register int i=lo; i<=hi; ++i) { l[now]=i; search(now+1,i,hi-i); l[now]=0; } return 0;}int main(){ scanf("%d%d%d",&n,&m,&p); fac[0]=1; for(register int i=1; i<=n; ++i) { fac[i]=(1ll*fac[i-1]*i)%p; } search(1,1,n); printf("%d\n",(int)((1ll*ans*quickpow(fac[n],p-2,p))%p)); return 0;}
发表评论
最新留言
关于作者
