zoukankan      html  css  js  c++  java
  • 【BZOJ1488】[HNOI2009]图的同构(Burside引理,Polya定理)

    【BZOJ1488】[HNOI2009]图的同构(Burside引理,Polya定理)

    题面

    BZOJ
    洛谷

    题解

    求本质不同的方案数,很明显就是群论这套理论了。
    置换一共有(n!)个,考虑如何对于任意一个置换求不动点数量。
    首先边存在或者不存在太麻烦了,我们假装所有边都已经存在,出现过的边和不存在的边用两种不同的颜色染色即可。这样子我们就假装所有的边都出现了,也就是一个完全图。
    显然循环是对于点而论的,但是这题同构是对于边而论的。那么我们对于一个点的循环,考虑它的两个顶点。这两个顶点只有两种不同情况,要么在同一个循环内,要么不在同一个循环内。考虑所有在同一个循环中的(n)点形成的完全图,那么它的边构成了(n/2)个循环,感性理解就是,我们把(n)个点拉成一排,把相邻距离为定值的点连上边,显然这样子会构成一个环,因为这个距离的定值(d)(n-d)是等价的,所以边构成了(n/2)个循环。
    考虑两个顶点不在一个循环内,那么构成循环必定是在一个点集中选择一条边连向另外一个点集,再从另外一个点集中选一条边连回来,我们把点集看成两个环,那么每次可以把环上所有的点旋转一下,那么旋转(lcm)次之后就转回来了,意味着这(lcm)条边必须相同,即构成一个循环,那么边的循环的个数就是(gcd)了。
    假设有边的置换,我们很容易知道不动点的数量就是(2^m),其中(m)是边置换的数量,显然你的个置换中的边的颜色都是相同的。
    这样一来,我们就把点置换转换为边置换了,这样就可以方便的计算了。
    (60)的拆分数大概是百万级别的,我们似乎是可以爆搜拆分数计算答案的。
    那么考虑一个拆分数实际上对应的方案数,这个排列组合计算一下就好了。
    我们假设有(k)个循环,每个的大小分别是(a_1,a_2,...),每个大小的置换个数是(num_1,num_2...)
    那么这种情况的贡献就是(frac{n!}{(prod a_i!)*(prod num_i!)}),原因就是,(n!)是所有方案,然后对于每个置换内,显然环的位置不影响,出去等价的环,然后对于每个等大小的置换,显然位置是可以交换的,那么除去阶乘的排列的影响。

    好了,这样子大概可以算完所有置换的不动点数量,然后除掉一个置换总数就对了,显然置换总数是(n!)

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    #define MOD 997
    #define MAX 75
    int n,ans,jc[MOD],inv[MOD],jv[MOD];
    int g[MAX][MAX],a[MAX],b[MAX],bin[MAX*MAX];
    void calc()
    {
    	int ret=jc[n],tot=0,sum=0;
    	for(int i=1;i<=n;++i)
    	{
    		ret=ret*jv[a[i]]%MOD;
    		for(int j=1;j<=a[i];++j)
    			ret=ret*inv[i]%MOD,b[++tot]=i;
    	}
    	for(int i=1;i<=tot;++i)sum+=b[i]/2;
    	for(int i=1;i<=tot;++i)
    		for(int j=i+1;j<=tot;++j)
    			sum+=g[b[i]][b[j]];
    	ret=ret*bin[sum]%MOD;
    	ans=(ans+ret)%MOD;
    }
    void dfs(int x,int sum)
    {
    	if(x==1){a[x]=n-sum;calc();return;}
    	for(int i=0;sum+i*x<=n;++i)
    		a[x]=i,dfs(x-1,sum+i*x);
    }
    int main()
    {
    	scanf("%d",&n);
    	jc[0]=jv[0]=inv[0]=inv[1]=bin[0]=1;
    	for(int i=2;i<MOD;++i)inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD;
    	for(int i=1;i<MOD;++i)jc[i]=jc[i-1]*i%MOD;
    	for(int i=1;i<MOD;++i)jv[i]=jv[i-1]*inv[i]%MOD;
    	for(int i=1;i<MAX*MAX;++i)bin[i]=bin[i-1]*2%MOD;
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=n;++j)
    			g[i][j]=__gcd(i,j);
    	dfs(n,0);ans=ans*jv[n]%MOD;
    	printf("%d
    ",ans);return 0;
    }
    
  • 相关阅读:
    sql 拼写
    五、URL Routing介绍
    六、防止JavaScript注入攻击
    七、创建自定义的HTML Helper
    二、理解Models,Views,Controllers
    一、使用ASP.NET MVC创建应用程序
    自己写的临时表应用
    四、理解视图层,视图数据和HTML辅助
    三、理解控制器、控制器动作和ActionResults
    Extensions (扩展)方法
  • 原文地址:https://www.cnblogs.com/cjyyb/p/9775407.html
Copyright © 2011-2022 走看看