P4727 [HNOI2009]图的同构记数

题目链接


将边是否存在问题转化为黑白染色问题。

设置换群为\(S\),显然,\(|S|=n!\),考虑一个置换会产生多少边的循环。

设一个置换划分为\(k\)个循环,分别为\(A_1,A_2, \cdots A_k\)

在点循环\(A_i\)中,以距离划分,共有\(\frac{|A_i|}{2}\)种边,对\(ans\)的贡献为\(2^{\frac{|A_i|}{2}}\)

对于点循环\(A_i,A_j\),若\((i,j)\)有边相连,则构成长度为\(lcm(|A_i|,|A_j|)\)的边循环,共有\(\frac{|A_i||A_j|}{lcm(|A_i|,|A_j|)}=gcd(|A_i|,|A_j|)\)种循环。

所以在一个点的置换中,对\(ans\)的贡献为

\(\prod\limits_{i=1}^k{2^{\frac{|A_i|}{2}}\prod\limits_{j=i+1}^{k}{2^{gcd(|A_i|,|A_j|)}}}\)

考虑划分置换。

我们只需要知道置换中循环的大小,可通过拆分自然数的方式先将置换大小得出来。

在一个循环\(A_i\)中,为了其中元素构成一个循环,有\((|A_i|-1)!\)中排列方式。

但是,对于长度相同的循环,交换一下我们就会认为组成新的循环,设\(buk_i\)为长度为\(i\)的循环的个数,则我们还有除以\(\prod\limits_{i=1}^{n}{buk_i!}\)

\(\therefore ans=\frac{1}{|S|}\sum\limits_{自然数划分}{n!\frac{\prod\limits_{i=1}^k{2^{\frac{|A_i|}{2}}\prod\limits_{j=i+1}^{k}{2^{gcd(|A_i|,|A_j|)}}}}{\prod\limits_{i=1}^k{|A_i|}\prod\limits_{i=1}^n{buk_i!}}}\)

\(n!\)\(\frac{1}{|S|}\)抵消,

\(ans=\sum\limits_{自然数划分}{\frac{\prod\limits_{i=1}^k{2^{\frac{|A_i|}{2}}\prod\limits_{j=i+1}^{k}{2^{gcd(|A_i|,|A_j|)}}}}{\prod\limits_{i=1}^k{|A_i|}\prod\limits_{i=1}^n{buk_i!}}}\)

\({\frak{code:}}\)

#include<bits/stdc++.h>
#define IL inline
using namespace std;
const int N=65,p=997;
int n,ans,buk[N],a[N],fac[N],gcd[N][N],er[N];
int Gcd(int x,int y){return y?Gcd(y,x%y):x;}
void pre(){
	fac[0]=er[0]=1;
	for(int i=1;i<=n;++i) fac[i]=fac[i-1]*i%p;
	for(int i=1;i<=n;++i)
	  for(int j=i;j<=n;++j)
	    gcd[i][j]=gcd[j][i]=Gcd(i,j);
	for(int i=1;i<=n;++i) er[i]=er[i-1]*2%p;
}
IL int ksm(int a,int b){
	int c=1;
	while(b){
		if(b&1) c=c*a%p;
		a=a*a%p,b>>=1;
	}
  return c;
}
void calc(int m){
	int res=1,inv=1;memset(buk,0,sizeof(buk));
	for(int i=1;i<=m;++i) res=res*er[a[i]>>1]%p;
	for(int i=1;i<=m;++i)
	  for(int j=i+1;j<=m;++j)
	    res=res*er[gcd[a[i]][a[j]]]%p;
	for(int i=1;i<=m;++i) ++buk[a[i]],inv=inv*a[i]%p;
	for(int i=1;i<=n;++i) inv=inv*fac[buk[i]]%p;
	ans=(ans+res*ksm(inv,p-2)%p)%p;
}
void dfs(int k,int i,int sum){
	if(sum==n) return calc(k-1);
	for(;i<=n-sum;++i) a[k]=i,dfs(k+1,i,sum+i);
}
int main()
{
	scanf("%d",&n),pre();
	if(n==0){puts("1");return 0;}
	dfs(1,1,0),cout<<ans<<endl;
	return 0;
}
posted @ 2020-01-03 00:07  (o-ωq)).oO  阅读(184)  评论(0编辑  收藏  举报