• bzoj 1488: [HNOI2009]图的同构


    Description

    求两两互不同构的含n个点的简单图有多少种。
    简单图是关联一对顶点的无向边不多于一条的不含自环的图。
    a图与b图被认为是同构的是指a图的顶点经过一定的重新标号以后,a图的顶点集和边集能完全与b图一一对应。

    Solution

    转化模型:给边 (0/1) 染色,如果为 (1) 则代表选择,求方案数
    考虑一下这个题的置换实际上是边置换,而把边置换用到的节点集合拿出来,发现这些点集合也可以是点置换
    于是我们想到用把边置换按照点置换归类

    于是复杂度就从边数降到点数了,枚举点置换的复杂度实际上是求 (n) 的划分的复杂度,(n=60) 时,约为 (10^6) 级别
    问题在于如何统计点置换中的边循环个数

    利用 (polya) 公式:
    (L=frac{1}{|G|}*sum_{i∈ G} 2^k_i)
    (k_i) 是每一个置换的循环的个数

    考虑边的两端点都在同一点置换的边置换个数
    这个置换的要求是:经过每一个点至少一次且形成一个环
    观察一个例子
    题面
    我们可以走相邻的点(也就是红边)
    或者隔两个点走一步(绿边)
    然后发现隔三个点走的走出来的和绿边一模一样,于是算重了
    于是总结出规律: 隔 (i)(n-i) 个点走出来的边置换是一样的,所以边置换个数就是 (frac{n}{2})

    再考虑两端点都不在同以点置换的边置换个数
    我们反复横跳两个点置换,直到某个时刻都遍历完,循环的大小为 (lcm(a,b)),(a,b) 为两个循环的大小
    因为总边数是 (a*b) ,所以循环个数就为 (frac{a*b}{lcm(a,b)}=gcd(a,b))

    最后就只要统计点置换的个数了
    容易发现就是: (frac{n!}{size[1]*size[2]*...*size[n]*t[1]!*t[2]!*...*t[n]!})
    (size) 表示每一个点置换的大小, (t[i]) 表示大小为 (i) 的点置换的个数
    因为点是一个环,所以多枚举了 (size) 次,另外对于大小为 (i) 的连通块出现了多次,相当于一个可重排列,除以 (t[1]!)

    最后总置换个数是 (n!),要记得除

    #include<bits/stdc++.h>
    using namespace std;
    const int mod=997,N=65;
    int n,num=0,sz[N],w[N],Fac[N],ans=0;
    inline int qm(int x,int k){
        int sum=1;
        while(k){
            if(k&1)sum=1ll*sum*x%mod;
            x=1ll*x*x%mod;k>>=1;
        }
        return sum;
    }
    inline int gcd(int a,int b){return b?gcd(b,a%b):a;}
    inline void dfs(int B,int res){
        if(!res){
            int sam=1,tot=0;
            for(int i=1;i<=num;i++){
                tot+=sz[i]*(sz[i]-1)/2*w[i]+w[i]/2*sz[i];
                for(int j=i+1;j<=num;j++)
                    tot+=sz[i]*sz[j]*gcd(w[i],w[j]);
            }
            for(int i=1;i<=num;i++)
                sam=sam*Fac[sz[i]]*qm(w[i],sz[i])%mod;
            sam=Fac[n]*qm(sam,mod-2)%mod;
            ans=(ans+qm(2,tot)*sam)%mod;
            return ;
        }
        if(B==n+1 || B>res)return ;
        dfs(B+1,res);
        for(int i=1;i*B<=res;i++){
            w[++num]=B;sz[num]=i;
            dfs(B+1,res-i*B);
            num--;
        }
    }
    int main(){
      scanf("%d",&n);
      Fac[0]=1;for(int i=1;i<=n;i++)Fac[i]=Fac[i-1]*i%mod;
      dfs(1,n);
      ans=ans*qm(Fac[n],mod-2)%mod;
      printf("%d
    ",ans);
      return 0;
    }
    
  • 相关阅读:
    block为什么用copy以及如何解决循环引用
    iOS证书失效
    基于AFNetWorking封装一个网络请求数据的类
    Xcode的内存清理
    block的用法以及block和delegate的比较(转发)
    React-Native 获取node.js提供的接口
    npm创建和发布模块
    React-Native之ViewPagerAndroid的使用
    使用.NET框架、Web service实现Android的文件上传(二)
    使用.NET框架、Web service实现Android的文件上传(一)
  • 原文地址:https://www.cnblogs.com/Yuzao/p/8486474.html
Copyright © 2020-2023  润新知