bzoj1478 Sgu282 Isomorphism
这道题也是一道不错的找规律polya原理的题
我们可以发现,对于两种点的置换,如果他们的循环节完全一样,那么他们对于边的置换的循环也完全一样。
我们又会发现,当一条边两侧的点位于同一循环中时,这些边构成的循环节共有${\frac{L_{i}}{2}}$个,$L_{i}$表示点的循环节长度
当两点位于两个循环中时,这些变构成的循环节共有$gcd(L_{i},L_{j})$个。
然后我们需要算出每种点的置换的方案数,这个写着不方便,看代码吧
于是我们可以dfs出每种点的置换,然后求出对应边的置换的循环节数以及方案数,最后上polya就好了!
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 #include <cmath> 6 #define int long long 7 using namespace std; 8 int n,m,mod; 9 int qp(int a,int b){ 10 int c=1; 11 while(b){ 12 if(b&1)c=c*a%mod; 13 a=a*a%mod; b>>=1; 14 }return c; 15 } 16 int l[66],C[66][66],fac[66],ny[66],g[66][66]; 17 int gcd(int a,int b){return !b?a:gcd(b,a%b);} 18 int all,cnt,sum,ans; 19 void dfs(int x,int last,int num){ 20 if(x==n+1){ 21 sum=0; 22 for(int i=1;i<=num;i++)sum+=l[i]/2; 23 for(int i=1;i<=num;i++) 24 for(int j=i+1;j<=num;j++)sum+=g[l[i]][l[j]]; 25 cnt=1; 26 for(int i=1,rest=n,j;i<=num;i++){ 27 j=i; 28 while(j<=num&&l[j]==l[i]){ 29 (cnt*=C[rest][l[j]]*fac[l[j]-1]%mod)%=mod; 30 rest-=l[j];j++; 31 } 32 (cnt*=ny[j-i])%=mod; 33 i=j-1; 34 35 } 36 (all+=cnt)%=mod; 37 (ans+=cnt*qp(m,sum)%mod)%=mod; 38 return ; 39 } 40 for(int i=last;x+i<=n+1;i++){ 41 l[num+1]=i; 42 dfs(x+i,i,num+1); 43 } 44 } 45 signed main(){ 46 scanf("%lld%lld%lld",&n,&m,&mod); 47 fac[0]=1; 48 for(int i=1;i<=n;i++){ 49 fac[i]=i*fac[i-1]%mod; 50 ny[i]=qp(fac[i],mod-2); 51 } 52 for(int i=1;i<=n;i++) 53 for(int j=1;j<=n;j++)g[i][j]=g[j][i]=gcd(i,j); 54 for(int i=0;i<=n;i++){ 55 C[i][0]=1; 56 for(int j=1;j<=i;j++) 57 C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod; 58 } 59 dfs(1,1,0); 60 ans=(ans*qp(all,mod-2))%mod; 61 printf("%lld\n",ans); 62 return 0; 63 }
人生如梦亦如幻 朝如晨露暮如霞。