Burnside 引理 与 Pólya 定理 学习笔记
为了防止明天就把好不容易听完的东西都还给 rabbit_lb 了,还是记一点吧。
1. 群论基础
1.1 群(group) 的定义
给定集合 和 上的二元运算 ,满足下列条件称之为群:
- 封闭性:若 ,则 。
- 结合律:对于任意 ,有 。
- 单位元:存在单位元 ,。
- 逆元:对于任意 ,存在 ,使得 。记为 。
1.2 一些概念
-
群元素个数有限则称为有限群,无限则称为无限群。
-
有限群 的元素个数叫做群的阶,记做 。
-
设 和 上的二元运算 构成一个群, 是 的子集,且 在原有运算下也是一个群,则 为 的一个子群。
-
若群 的任意两元素均满足交换律,则称 为交换群(Abel 群)。
1.3 群的性质
- 单位元唯一:
- 消去律:
- 每个元的逆元唯一:反证,若 ,则 ,即 。
- 若 有限,且 ,则存在最小正整数 ,使得 ,且 。 称为 的阶。
2. 置换群
2.1 置换
到自身的一个映射称为 阶置换,表示为 ,其中 是 的一个排列。
阶置换共有 个,同一个置换有 中表示方法,如 。 阶置换也可以看作 上的一元运算。
设 ,则定义置换乘法 。
置换乘法不满足交换律,但满足结合律。
2.2 置换群
上由多个置换组成的集合,在 2.1 的乘法定义下构成的群,称为置换群。
- 封闭性:
- 结合律:由 2.1 知置换乘法满足结合律。
- 单位元:
- 逆元:
上的所有( 个)置换构成的群,称为 阶对称群,记作 。平时所说的 上的一个置换群,一定是 的子群。
2.3 循环
2.3.1 置换的循环表示
置换 可以写作 的形式,称为置换的循环表示。E.g. ,。
称为 阶循环,有 种表示方法。
通常情况下,我们可以忽略所有阶为 的循环。两个不相交的循环之间满足交换律。
定理:任意置换可表示成若干不相交循环的积。
证明:考虑令置换 向 连边,图由若干个环构成。显然每个环都可以表示成一个循环。
2.3.2 共轭类
我们设置换 的循环表示为 。设 阶循环出现的次数为 。
那么置换 的格式为 。E.g. 的格式为 。
则 中所有相同格式的置换构成一个共轭类。
定理: 中 所在的共轭类元素个数为 。
可以这样理解这个式子:
- 一个长度为 的循环共有 种表示, 个长度为 的循环有 种表示;
- 对互不相交的 个循环枚举全排列,共有 种表示。
2.3.3 对换与奇偶置换
阶循环叫做对换。
定理:任意循环都可以表示为若干对换的积。
推柿子:
那么进一步地,有分解 。注意每个置换的分解不唯一。
若一个置换能分解为奇数个对换之积,则为奇置换;否则为偶置换。
Warning. 置换相乘的奇偶性类似于自然数加法,而非自然数乘法:奇 x 奇 = 偶,奇 x 偶 = 奇。
3. Burnside 引理
3.1 等价类与 不动置换类
设 是 上的一个置换群,。 中使 元素保持不变的置换全体,称为 不动置换类,记作 。
定理:置换群 的 不动置换类 是 的子群。
- 封闭性: 怎么置换都不动。
- 结合性:显然。
- 单位元: 的单位元也在 中。
- 逆元: 中的置换 在 中的逆元 也在 中。
置换 使图像 变为 ,则称 和 属于同一个等价类。设 所在的等价类记为 。
如图,将正方形四个顶点红蓝染色,等价类个数为 。(每行是一个等价类)
3.2 轨道稳定子定理
定理:设 是 上的一个 置换群, 是 在 的作用下包含 的等价类, 是 不动置换类。有 。
证明:每个等价类有 个元素,同时因为它们属于同一等价类,每个元素的 相同。因此这些 覆盖了整个 ,即每个等价类都有 。
3.3 Burnside 引理
将上式变形,有:
仔细想一下会发现 就是等价类个数。
然而问题并没有解决,因为 不好求。进一步地,我们定义 表示在置换 的作用下不动点的个数,即长度为 的循环个数。那么等价类个数为:
这个式子就是 Burnside 引理。
4. Pólya 定理
Pólya 定理是 Burnside 引理的推广,应用于 染色问题 的 循环同构 方案计数。
设 是 个对象 的一个置换群, 是置换 的循环的个数,用 种颜色对 个对象着色,着色方案数为
接下来用一个例题说明该定理的具体用法。
用火柴搭一个足球,有多少种方案?
Tips: 足球有 个顶点, 条棱, 个五边形, 个六边形。
- 不动: 种置换, 种染色;
- 五边形对五边形转: 种置换, 种染色;
- 六边形对六边形转: 种置换, 种染色;
- 棱中点对棱中点转: 种置换, 种染色(一定都会变)。
则本质不同的方案数为 。
5. 例题
P4980【模板】Polya 定理
板子。发现置换只有旋转,考虑枚举旋转的角度,有:
枚举 ,可以变成
也就是
暴力计算欧拉函数即可通过。
Code
#define int long long
const int mod=1e9+7;
int T,n;
il int qpow(int n,int k=mod-2)
{
int res=1;
for(;k;n=n*n%mod,k>>=1) if(k&1) res=res*n%mod;
return res;
}
il int phi(int x)
{
int res=x;
for(int i=2;i*i<=x;i++)
{
if(x%i==0) res=res/i*(i-1);
while(x%i==0) x/=i;
}
if(x>1) res=res/x*(x-1);
return res;
}
signed main()
{
T=read();
while(T--)
{
n=read();
int ans=0;
for(int d=1;d*d<=n;d++) if(n%d==0)
{
(ans+=qpow(n,d)*phi(n/d)%mod)%=mod;
if(d*d!=n) (ans+=qpow(n,n/d)*phi(d)%mod)%=mod;
}
ans=ans*qpow(n)%mod;
printf("%lld\n",ans);
}
return 0;
}
CF1065E Side Transmutations
首先可以发现翻转是可以抵消的。如果我们操作 ,再操作 ,再操作 ,这相当于只操作了一个 。也就是说,想要得到最终状态我们只关心每个 被操作次数的奇偶性。
考虑 Polya 定理,但是发现不动点看起来不好算。
进一步对 进行转化,考虑将字符串分成形如 的若干段。那么我们统计不动点的时候只关心每一段是否被翻转。
不难看出,字符串每段的翻转状态与 的操作次数奇偶性序列构成双射。设 表示第 段的长度。
那么如果第 段被翻转了,这一段本身的贡献是 ;否则为 。而对于中间长度为 且永远不会被翻转的段,贡献恒为 。
故设 ,本质不同的方案数为
直接计算即可,时间复杂度为 。
Code
#define int long long
const int N=2e5+5,mod=998244353;
int n,m,A,b[N];
int l[N];
il int qpow(int n,int k=mod-2)
{
int res=1;
for(;k;n=n*n%mod,k>>=1) if(k&1) res=res*n%mod;
return res;
}
signed main()
{
n=read(),m=read(),A=read();
for(int i=1;i<=m;i++) b[i]=read();
for(int i=1;i<=m;i++) l[i]=b[i]-b[i-1];
int sum=1,ans=0;
for(int i=1;i<=m;i++) sum=sum*(qpow(A,l[i])+qpow(A,2*l[i]))%mod;
ans=sum*qpow(A,n-2*b[m])%mod;
ans=ans*qpow(qpow(2,m))%mod;
printf("%lld\n",ans);
return 0;
}
ARC062F Painting Graphs with AtCoDeer
一个比较直观的性质是,一条边无论如何都转不出自己所在的边双连通分量。进一步地,发现实际上一条边转不出自己所在的点双。这似乎有点反直觉,但考虑下图,边无法从一个环移到另一个环:
那么根据这个结论,每个点双之间是独立的。分类讨论:
- 如果一条边的两个端点不在同一点双里,这条边对答案的贡献是 ;
- 如果点双是一个环,这是 Polya 定理的板子;
- 否则有结论:如果一个点双有至少两个环,所有边都可以任意交换。
考虑证明上述结论,我们只需证明存在一种方案,在不改变其他边的情况下交换两条边。
使用上图的做法可以交换两环交界处的两条边。对于不在两环交界处的边,可以先转到交界处再如此操作。
故设这个点双有 条边,对答案的贡献就是 条边涂 个颜色,不区分顺序的方案数。经典插板法,为 。
对以上三种情况分别计算贡献即可。
Code
#define int long long
const int N=205,mod=1e9+7;
int n,m,k;
vector<int> e[N];
int dfn[N],low[N],tot,num;
vector<int> t[N],q;
void tarjan(int u,int fa)
{
dfn[u]=low[u]=++tot; q.push_back(u);
for(auto v:e[u]) if(v^fa)
{
if(!dfn[v])
{
tarjan(v,u),low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u])
{
num++;
while(q.back()!=u)
{
int x=q.back();
t[num].push_back(x),q.pop_back();
if(x==v) break;
}
t[num].push_back(u);
}
}
else low[u]=min(low[u],dfn[v]);
}
}
int bel[N];
il int qpow(int n,int k=mod-2)
{
int res=1;
for(;k;n=n*n%mod,k>>=1) if(k&1) res=res*n%mod;
return res;
}
il int solve(int m)
{
int res=0;
for(int i=1;i<=m;i++) res=(res+qpow(k,__gcd(i,m)))%mod;
res=res*qpow(m)%mod; return res;
}
int jc[N],inv[N];
il void init(int mx)
{
jc[0]=inv[0]=1;
for(int i=1;i<=mx;i++) jc[i]=jc[i-1]*i%mod;
inv[mx]=qpow(jc[mx]);
for(int i=mx-1;i;i--) inv[i]=inv[i+1]*(i+1)%mod;
}
il int C(int n,int m)
{
if(m>n) return 0;
return jc[n]*inv[n-m]%mod*inv[m]%mod;
}
signed main()
{
n=read(),m=read(),k=read();
for(int i=1;i<=m;i++)
{
int u=read(),v=read();
e[u].push_back(v),e[v].push_back(u);
}
init(m+k);
int ans=1;
for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i,0);
for(int i=1;i<=num;i++)
{
for(auto x:t[i]) bel[x]=i;
int cnt=0;
for(auto u:t[i])
for(auto v:e[u]) if(bel[v]==i) cnt++;
cnt>>=1;
m-=cnt;
if(cnt>t[i].size()) ans=ans*C(cnt+k-1,k-1)%mod;
else if(cnt==t[i].size()) ans=ans*solve(cnt)%mod;
else ans=ans*qpow(k,cnt)%mod;
}
printf("%lld\n",ans);
return 0;
}
[HNOI2009] 图的同构计数
我们可以把每条边选与不选看作对一个完全图上的边黑白染色,这与原题意是等价的。
使用 Polya 定理,先枚举一个置换,并把置换写成循环的形式。考虑对在该置换下不变的染色方案进行计数。
根据一条边的两个端点是否在同一个循环内,分类讨论:
- 如图,若一条边的两个端点属于同一循环,那么它应当与循环内所有长度相等的边同色。
那么一个长度为 的循环,其内部共有 类等价的边。 - 否则,若一条边连接了两个不同循环,设它们的长度分别为 。那么一条边需要做 次置换才能回到它原来的位置,即等价类个数为 。
综上,一个 序列对 polya 求和式子的贡献为
发现这只与 数组有关而与具体的置换无关,考虑只枚举 数组,并计算有多少种对应的置换。由 2.3.2 可知,设 表示 的 的个数,对应的置换数为
总答案为
其中 见上文。时间复杂度为 的无序拆分数。
Code
#define int long long
const int N=205,mod=997;
int n,ans,tot,b[N],c[N];
il int qpow(int n,int k=mod-2)
{
int res=1;
for(;k;n=n*n%mod,k>>=1) if(k&1) res=res*n%mod;
return res;
}
il int solve()
{
int res=0;
for(int i=1;i<=tot;i++)
{
res=(res+(b[i]>>1));
for(int j=1;j<i;j++) res=(res+__gcd(b[i],b[j]));
}
return res;
}
void dfs(int sum)
{
if(!sum)
{
int k=solve();
int v=1;
for(int i=1;i<=n;i++) c[i]=0;
for(int i=1;i<=tot;i++) v=v*b[i]%mod,c[b[i]]++;
for(int i=1;i<=n;i++)
for(int j=1;j<=c[i];j++) v=v*j%mod;
ans=(ans+qpow(2,k)*qpow(v)%mod)%mod;
return;
}
for(int k=b[tot];k<=sum;k++)
{
b[++tot]=k,dfs(sum-k);
tot--;
}
}
signed main()
{
n=read();
b[0]=1; dfs(n);
printf("%lld\n",ans);
return 0;
}
P4128 [SHOI2006] 有色图
与上题同理,将 换为 即可。
本文来自博客园,作者:樱雪喵,转载请注明原文链接:https://www.cnblogs.com/ying-xue/p/burnside-polya.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现