bzoj1488 [HNOI2009]图的同构(群论+组合数学+polya)

本文探讨了计算不同构的n个点的简单图种类数的方法。通过将问题转化为完全图上的边2染色计数,并利用Polya定理解决。文章详细介绍了如何通过枚举点置换的循环节长度来解决此问题,最终给出了解决方案和实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

感觉我的排列组合需要回炉重造一下了qaq

求不同构的n个点的简单图的种类数。

因为每条边有存在或不存在两种选择,所以可以转化为完全图上对边的2染色计数。但是我们只能处理点的置换(就是个n次对称群),而对于边的置换我们就很gg了。我们考虑对于一个点的置换,能否直接得出这种情况下的边的置换。

每条边无非两种情况:
1、两点处在同一循环节中
2、两点处在不同循环节中
我们考虑一个长度为x的循环节内部,边的循环节个数为n/2.
(个人理解是n(n1)/2/n,即边的个数/n上取整,可以画画图找规律)
我们考虑两个长度分别为x,y的循环节之间的边,循环节个数为gcd(x,y)
(个人理解是xy/lcm(x,y),即边数/边循环节长度)

因此我们可以直接由一个点置换得到边置换的循环节个数m,那么不动点个数就是2m,因此根据polya原理,最后的答案就是2mn!

我们考虑枚举点置换,是O(n!)的,gg

我们发现边置换的循环节个数其实只与点置换的循环节长度有关,因此我们考虑枚举点置换的循环节长度,即枚举n的划分,我们钦定它有序,从小到大枚举,假设我们现在枚举了n的一个划分为l1,l2,…lk,长度为i的循环节个数有si个,则这种划分所代表的点置换的个数为

n!l1l2...lks1!s2!...sq!

除以li是因为每一个循环节的重复圆排列,除以si是因为相同大小的循环节的重复排列。
然后此题就解决了orz,复杂度大概是n的有序整数拆分数,极限情况f(60)=966467,大概还行。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
#define inf 0x3f3f3f3f
#define ll long long
#define mod 997
#define N 70
inline char gc(){
    static char buf[1<<16],*S,*T;
    if(S==T){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x*f;
}
int n,inv[N],fac[N],ifac[N],len[N],tot=0,num[N],ans=0;
inline void init(){
    inv[1]=1;fac[1]=1;ifac[1]=1;
    for(int i=2;i<=n;++i) inv[i]=inv[mod%i]*(mod-mod/i)%mod;
    for(int i=2;i<=n;++i) fac[i]=fac[i-1]*i%mod,ifac[i]=ifac[i-1]*inv[i]%mod;
}
inline int ksm(int x,int k){
    int res=1;for(;k;k>>=1,x=x*x%mod) if(k&1) res=res*x%mod;return res;
}
inline int gcd(int x,int y){return y?gcd(y,x%y):x;}
inline void dfs(int val,int left){//枚举n的划分n1+n2+...=n,钦定n1<=n2<=...,该填val了,还剩left可以划分
    if(left==0){
        int m=0,cnt=fac[n];
        for(int i=1;i<=tot;++i){
            m+=len[i]/2*num[i]+num[i]*(num[i]-1)/2*len[i];
            for(int j=i+1;j<=tot;++j)
                m+=num[i]*num[j]*gcd(len[i],len[j]);
            (cnt*=ksm(inv[len[i]],num[i])*ifac[num[i]])%=mod;
        }ans+=ksm(2,m)*cnt;ans%=mod;return;
    }if(val>left) return;dfs(val+1,left);
    for(int i=1;i*val<=left;++i){
        len[++tot]=val;num[tot]=i;dfs(val+1,left-i*val);--tot;
    }
}
int main(){
//  freopen("a.in","r",stdin);
    n=read();init();dfs(1,n);
    printf("%d\n",ans*ifac[n]%mod);
    return 0;
}
### 论和群论的概念及应用 #### 1. 论的核心概念 论是一种研究由节点(顶点)和连接这些节点的边组成的数学模型。它主要用于建模各种离散结构之间的关系[^2]。 - **基本元素**: $ G=(V,E) $ 中,$ V $ 表示顶点集合,$ E $ 表示边集合。 - **路径与连通性**:路径是指一系列相连的边;如果任意两个顶点间存在路径,则该为连通。 - **权重与方向**:加权赋予每条边一个数值,有向则规定边的方向。 ```python import networkx as nx G = nx.Graph() # 创建无向 G.add_edge('A', 'B') # 添加边 A-B print(G.edges()) # 输出边列表 [('A', 'B')] ``` #### 2. 群论的核心概念 群论是代数的一个分支,专注于研究具有特定运算规则的集合——即“群”。它是理解对称性和变换的关键工具[^1]。 - **定义**:设 $ (G, \cdot) $ 是一个带有二元运算 $\cdot$ 的集合,若满足封闭性、结合律、单位元的存在以及逆元的存在,则称为群。 - **重要类型**: - 循环群:如 C3_irrep_basis 所属的循环群 $ C_3 $,仅由单一生成元构成[^1]。 - 二面体群:如 D8_irrep_basis 对应的二面体群 $ D_8 $,描述平面正多边形的对称操作[^1]。 #### 3. 论的应用场景 论在计算机科学中有广泛应用,特别是在网络分析、算法设计等领域[^2]。 - **社交网络分析**:通过来表示人际关系网,帮助识别社区结构或关键人物。 - **最短路径问题**:Dijkstra 和 Floyd-Warshall 算法解决加权上的距离最小化问题。 - **匹配与覆盖**:匈牙利算法求解最大匹配问题,在任务分配中至关重要。 #### 4. 群论的应用场景 群论不仅限于纯数学领域,还深入物理学、化学乃至计算机形学等多个学科[^1]。 - **晶体对称性分析**:基于不可约表示预测材料能带结构中的简并现象。 - **分子振动光谱**:利用群论区分红外活跃与非活跃振动能级。 - **量子态分类**:借助张量积规则推导粒子相互作用的选择定则。 #### 5. 论与群论的关系 两者虽分属不同数学分支,但在某些情况下可互相结合使用[^3]。 - **代数论**:运用矩阵理论探讨的特性,例如邻接矩阵反映的拓扑信息。 - **对称性研究**:当考虑高度对称性的时,往往涉及某种类型的群作用。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值