bzoj 1488: [HNOI2009]图的同构 (置换+dfs)

19 篇文章 0 订阅

1488: [HNOI2009]图的同构

Time Limit: 10 Sec   Memory Limit: 64 MB
Submit: 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

Sample Output

输出1
1

输出2
2

输出3
4

HINT

题目在这里 http://hi.baidu.com/fqq11679/blog/item/c277b9f8ff205e50252df2e9.html

Source

[ Submit][ Status][ Discuss]

题解:置换+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);
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值