1488: [HNOI2009]图的同构
Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 535 Solved: 344
[ Submit][ Status][ Discuss]
Description
求两两互不同构的含n个点的简单图有多少种。
简单图是关联一对顶点的无向边不多于一条的不含自环的图。
a图与b图被认为是同构的是指a图的顶点经过一定的重新标号以后,a图的顶点集和边集能完全与b图一一对应。
Input
输入一行一个整数N,表示图的顶点数,0<=N<=60
Output
输出一行一个整数表示含N个点的图在同构意义下互不同构的图的数目,答案对997取模。
Sample Input
输入1
1
输入2
2
输入3
3
1
输入2
2
输入3
3
Sample Output
输出1
1
输出2
2
输出3
4
1
输出2
2
输出3
4
HINT
题目在这里 http://hi.baidu.com/fqq11679/blog/item/c277b9f8ff205e50252df2e9.html
Source
题解:置换+dfs
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 103
#define p 997
#define LL long long
using namespace std;
int n,num[N],k[N];
LL cnt,c[N][N],ans,d[N],jc[N],inv[N],a[N],g[N][N],b[N*N];
LL quickpow(LL num,int x)
{
LL ans=1; LL base=num%p;
while (x) {
if (x&1) ans=ans*base%p;
x>>=1;
base=base*base%p;
}
return ans;
}
void init(int n)
{
for (int i=0;i<=n;i++) c[i][0]=1;
for (int i=1;i<=n;i++)
for (int j=1;j<=i;j++) c[i][j]=(c[i-1][j]+c[i-1][j-1])%p;
jc[0]=1;
for (int i=1;i<=n;i++) jc[i]=jc[i-1]*i%p;
for (int i=1;i<=n;i++) inv[i]=quickpow(jc[i],p-2);
b[0]=1;
for (int i=1;i<=n*n;i++) b[i]=b[i-1]*2%p;
}
int gcd(int x,int y){
if (g[x][y]) return g[x][y];
int r;
while (y){
r=x%y;
x=y; y=r;
}
return g[x][y]=x;
}
void dfs(int x,int now)
{
if (!now) {
int n1=x-1; int sum=0;
for (int i=1;i<=n1;i++)
for (int j=i+1;j<=n1;j++) sum+=gcd(a[i],a[j]);
for (int i=1;i<=n1;i++) sum+=a[i]/2;
LL tot=1; int cnt=n;
for (int i=1;i<=n1;i++)
tot=tot*c[cnt][a[i]]%p,cnt-=a[i];
for (int i=1;i<=n;i++)
if (k[i]) tot=tot*inv[k[i]]%p;
for (int i=1;i<=n1;i++) tot=tot*jc[a[i]-1]%p;
ans=(ans+tot*b[sum])%p;
//cout<<tot<<" "<<sum<<endl;
return;
}
for (int i=a[x-1];i<=now;i++) {
k[i]++; a[x]=i;
dfs(x+1,now-i);
k[i]--;
}
}
int main()
{
scanf("%d",&n);
if (n==1) {
printf("1\n");
return 0;
}
a[0]=1;
init(n); dfs(1,n);
ans=ans*quickpow(jc[n],p-2)%p;
printf("%lld\n",ans);
}