知道點的順序,邊的順序也能確定下來,所以對於點置換可以算出相應的邊置換計算
可我們總不能枚舉全排列,我們可以進一步發現,結構相同的點置換對應的邊置換肯定是一樣的(廢話),可以搜出所有不同結構的點置換,發現時點置換個數不到
當前搜到點置換,那麼需要計算邊置換循環個數
連接不同點循環循環i,j的邊:一個循環覆蓋條邊,所以個循環(畫畫圖更容易理解一點)
點循環內部的邊:循環數爲(因爲一半會重)
所以總循環數
循環數量容易推得爲,其中表示循環長度爲的循環個數
剩下的套Polya就行了
代碼如下:
#include<bits/stdc++.h>
#define int long long
#define N 120
using namespace std;
inline int read(){
int x=0,f=1;char c;
do c=getchar(),f=c=='-'?-1:f; while(!isdigit(c));
do x=(x<<3)+(x<<1)+c-'0',c=getchar(); while(isdigit(c));
return x*f;
}
typedef long long LL;
int n,m,p,s[N],a[N],jc[N],gd[N][N];
LL ans;
LL gcd(LL a,LL b){
return b?gcd(b,a%b):a;
}
inline LL qpow(LL x,int k){
LL sum=1;
while(k){
if(k&1) sum=sum*x%p;
x=x*x%p;
k>>=1;
}
return sum;
}
void dfs(int x,int cnt,int pre){
if(cnt==n){
LL t=0,pp=1;
for(int i=1;i<x;i++)
t+=s[i]/2;
for(int i=1;i<x;i++)
for(int j=i+1;j<x;j++)
t+=gd[s[i]][s[j]];
for(int i=1;i<=n;i++) (pp*=jc[a[i]])%=p;
for(int i=1;i<x;i++) (pp*=s[i])%=p;
// cout<<pp<<" "<<t<<endl;
(ans=ans+jc[n]*qpow(pp,p-2)%p*qpow(m,t)%p)%=p;
return;
}
for(int i=pre;i<=n-cnt;i++){
s[x]=i;a[i]++;
dfs(x+1,cnt+i,i);
a[i]--;
}
}
main(){
n=read();m=read();p=read();
for(int i=jc[0]=1;i<=n;i++) jc[i]=jc[i-1]*i%p;
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j++) gd[i][j]=gd[j][i]=gcd(i,j);
dfs(1,0,1);
printf("%lld\n",ans*qpow(jc[n],p-2)%p);
return 0;
}