「算法笔记」Polya 定理
一、前置概念
接下来的这些定义摘自 置换群 - OI Wiki。
1. 群
若集合 S≠∅ 和 S 上的运算 ⋅ 构成的代数结构 (S,⋅) 满足一下性质:
-
封闭性:∀a,b∈S,a⋅b∈S。
-
结合律:∀a,b,c∈S,(a⋅b)⋅c=a⋅(b⋅c)。
-
单位元:∃e∈S,∀a∈S,e⋅a=a⋅e=a。
-
逆元:∀a∈S,∃b∈S,a⋅b=b⋅a=e,称 b 为 a 的逆元,记为 a−1。
则称 (S,⋅) 为一个 群。例如,整数集合整数集的加法 (Z,+) 构成一个群,单位元是 0,一个整数的逆元是它的相反数。
子群:若 (S,⋅) 是群,T 是 S 的非空子集,且 (T,⋅) 也是群,则称 (T,⋅) 是 (S,⋅) 的 子群。
2. 置换
有限集合到自身的双射(即一一对应)称为置换。集合 S={a1,a2,⋯,an} 上的置换可以表示为
f=(a1,a2,⋯,anap1,ap2,⋯,apn)
意为将 ai 映射为 api,其中 p1,p2,⋯,pn 是 1,2,⋯,n 的一个排列。显然 S 上所有置换的数量为 n!。
3. 置换的乘法
对于两个置换 f=(a1,a2,⋯,anap1,ap2,⋯,apn) 和 g=(ap1,ap2,⋯,apnaq1,aq2,⋯,aqn),f 和 g 的乘积记为 f∘g,其值为 f∘g=(a1,a2,…,anaq1,aq2,…,aqn)
简单来说就是先后经过 f 的映射,再经过 g 的映射。
4. 置换群
可以理解为是将置换作为元素的群。
易证,集合 S 上的所有置换关于「置换的乘法」满足封闭性(置换的乘积也是置换)、结合律、有单位元(恒等置换,即每个元素映射成它自己)、有逆元(交换置换表示中的上下两行),因此构成一个群。
这个群的任意一个 子群 即成为 置换群。
5. 循环置换
循环置换是一类特殊的置换。记
(a1,a2,⋯,am)=(a1,a2,⋯,am−1,ama2,a3,⋯,am,a1)
为 m 阶循环。若两个循环置换不含有相同的元素,则称它们是 不相交 的。
有如下定理:任意一个置换都可以分解为若干不相交的循环置换的乘积,例如
(a1,a2,a3,a4,a5a3,a1,a2,a5,a4)=(a1,a3,a2)∘(a4,a5)
置换的 循环节数 是上述表示中循环置换的个数。如上述例子中循环节数为 2。
如果把元素视为图的节点,映射关系视为有向边,则每个节点的入度和出度都为 1,因此形成的图形必定是若干个环的集合,而一个环即可用一个循环置换表示。
二、Burnside 引理
Update on 2021.6.1
修改了表述,应该会清楚好懂一点。
n 个珠子的项链做黑白二染色,2n 种。
但是项链可以旋转、对称,这些操作下相同的应该视为同一种。比如三个珠子时有 4 种:白白白,黑黑黑,白白黑,黑黑白。
Burnside 定理:
操作:旋转,对称,不变……(m 种操作)
等价类:可以通过操作互相转化的状态组成一个等价类。
想求:不同等价类总数。
对每一种操作 g,称在这种操作下不变的状态为 g 的不动点。
则不同等价类总数 =(∑g为m种操作之一g的不动点个数)m。
Update 前
Update 前的表述,可跳过 QAQ。
对于 ∀k∈S,记 k 所在的 等价类 为 Ek,Ek={x∣∃f∈G,f(k)=x},即 k 在 G 的作用下所能变化成的所有元素的集合。(若 ∃f∈G,f(x)=y,则 x 和 y 等价,所有互相等价的元素组成一个等价类。其中 f(x) 表示 x 进行置换 y 的结果)
对于 ∀k∈S,记 k 的 不动置换类 为 Zk,Zk={f∣f∈G,f(k)=k},即 G 中使 k 保持不变的置换的全体。
Burnside 引理:对于 ∀f∈G,记 D(f)=∑x∈S[f(x)=x],即 S 中在置换 f 下没有改变的元素的个数。设 L 为等价类个数。则:L=1|G|∑f∈GD(f)。证明。
换一种说法:对于一个置换 f,若一个着色方案 s 经过置换后不变,则称 s 为 f 的不动点。记 f 的不动点数目为 D(f),则等价类个数为所有 D(f) 的平均值。
等价类个数可以理解为 本质不同 的方案数。
正常一点的语言:本质不同的方案数为在每个置换下稳定不动的方案的总和除以总置换数。
三、Pólya 定理
容易发现,在 Burnside 引理中,要计算 D(f) 的值不是很容易。Pólya 定理 实质上是 Burnside 引理 的一个扩展。
Pólya 定理:设 G 是 p 个对象的置换群,用 m 种颜色涂染 p 个对象。对于 ∀f∈G,记 c(f) 为置换 f 的循环节数,则不同染色方案为:L=1|G|∑f∈Gmc(f)。
要得到在置换下稳定不动的方案,即把置换的每个循环节都染上相同的颜色,所以 D(f)=mc(f)。根据 Burnside 引理 就能得到 Pólya 定理。
它解决的问题有限。通常用 DP 或 组合数 来扩展此类问题。
黄队博客 中的表述(Polya 计数法):设 G 是 p 个对象的一个置换群,用 m 种颜色涂染 p 个对象,则不同染色方案为:
L=1|G|(mc(g1)+mc(g2)+⋅+mc(gs))
其中 G=g1,⋯,gs,c(gi) 为置换 gi 的环数。
四、应用与例题
对于没有接触过概念的读者来说,以上内容第一次读的时候可能会有些生涩,建议多读几遍。如果下面题目忘记写数据范围了那就去看原题吧 QwQ。
1. 常见置换的循环节数
-
1. 将长度为 n 的序列或环循环移 k 位:这个置换的循环节数为 gcd(n,k)。具体地,这个置换是由 gcd(n,k) 个 ngcd(n,k) 阶循环构成的。(lcm(n,k)k=ngcd(n,k))
-
2. 将长度为 n 的环沿对称轴反转。易知共有 n 种翻转置换。
-
若 n 为奇数,那么所有的轴会且只会经过一个环上的元素。每一个置换都是由一个 1 阶循环和 n−12 个 2 阶循环组成的,即 n 种翻转的循环节数均为 n+12。
-
否则,有两种情况:对称轴在两点之间时,对称轴左右两侧的点恰好一一配对,这 n2 个轴不经过环上的元素,这些轴对应的翻转置换由 n2 个 2 阶循环组成,即循环节数为 n2;对称轴恰好穿过两点,这 n2 个轴经过环上 2 个元素,这些轴对应的翻转置换由 2 个 1 阶循环和 n−22 个 2 阶循环组成,即循环节数为 n2+1。
-
2. Luogu P1446 [HNOI2008]Cards
题目大意:有 n 张牌,染三种颜色,每种颜色规定数目。给出 m 种不同的洗牌方法。
两种染色方法相同当且仅当其中一种可以通过任意的洗牌法(即可以使用多种洗牌法,而每种方法可以使用多次)洗成另一种。
求方案数对 p 取模的结果。
Solution:
把洗牌方法看作置换,染色方案看作“元素”。
输入数据保证任意多次洗牌都可用这 m 种洗牌法中的一种代替,且对每种洗牌法,都存在一种洗牌法使得能回到原状态。所以它满足封闭性,并且存在逆元。我们需要加上 恒等置换(即每个元素映射成它自己),使其存在单位元。由于单位元满足 a⋅e=a,所以并不会对答案产生影响。此时给出的置换是一个置换群,总置换数为 m+1。
因为染色存在数目限制,所以不能直接使用 Pólya 定理。
根据 Burnside 引理,本质不同的方案数为在每个置换下稳定不动的方案的总和(不动点数目之和)除以总置换数。
而要得到在置换下稳定不动的方案,那么就要把置换的每个循环节都 染上相同的颜色。
每个置换中都有若干个循环,根据给出的置换可以求出循环节数。考虑用 DP 求出每个循环节都染上相同的颜色,并且每种颜色的总和符合题目要求的方案总数。
所以,对于每一个置换,单独考虑每个循环染哪种颜色,可以通过一个类似 背包 的过程实现(把每一个循环看作一个物品,物品的重量为循环元素个数)。具体地,令 fi,j,k 表示三种颜色分别用了多少的方案数。
答案为在每个置换下稳定不动的方案的总和除以总置换数。
#include<bits/stdc++.h> #define int long long using namespace std; const int N=30,M=70; int n,R,B,G,m,mod,a[M],sz[M],cnt,f[N][N][N],ans; bool vis[M]; int mul(int x,int n,int mod){ //保证 mod 为质数,可以使用快速幂求逆元 int ans=mod!=1; for(x%=mod;n;n>>=1,x=x*x%mod) if(n&1) ans=ans*x%mod; return ans; } int solve(){ memset(vis,0,sizeof(vis)),cnt=0; for(int i=1;i<=n;i++){ if(vis[i]) continue; int x=i,len=0; while(!vis[x]) len++,vis[x]=1,x=a[x]; //求循环中的元素个数 sz[++cnt]=len; //cnt: 循环节数 } memset(f,0,sizeof(f)),f[0][0][0]=1; for(int t=1;t<=cnt;t++) //类似背包的过程 for(int i=R;i>=0;i--) for(int j=G;j>=0;j--) for(int k=B;k>=0;k--){ if(i>=sz[t]) (f[i][j][k]+=f[i-sz[t]][j][k])%=mod; if(j>=sz[t]) (f[i][j][k]+=f[i][j-sz[t]][k])%=mod; if(k>=sz[t]) (f[i][j][k]+=f[i][j][k-sz[t]])%=mod; } return f[R][G][B]; } signed main(){ scanf("%lld%lld%lld%lld%lld",&R,&G,&B,&m,&mod),n=R+G+B; for(int i=1;i<=m;i++){ for(int j=1;j<=n;j++) scanf("%lld",&a[j]); ans=(ans+solve())%mod; } for(int i=1;i<=n;i++) a[i]=i; //加上恒等置换(自己到自己) ans=(ans+solve())%mod,printf("%lld\n",ans*mul(m+1,mod-2,mod)%mod); return 0; }
3. Luogu P4980【模板】Pólya 定理
题目大意:给定一个 n 个点 n 条边的环,有 n 种颜色,给每个顶点染色,求本质不同的染色方案数,答案对 109+7 取模。
本质不同指的是不能通过旋转变得与其他染色方案相同。t≤103,n≤109。
Solution:
有 n 种循环移位,根据前面「常见置换的循环节数」中「将长度为 n 的环循环移 k 位:这个置换的循环节数为 gcd(n,k)」以及 Pólya 定理 可得:答案为 1nn∑k=1ngcd(n,k)。
推一下式子:
-
枚举 gcd 变为:1n∑d∣nnd×nd∑k=1[gcd(k,nd)=1]。
-
后面那个式子是欧拉函数,直接代入:1n∑d∣nndφ(nd)。
所以答案为 1n∑d∣nndφ(nd)。
#include<bits/stdc++.h> #define int long long using namespace std; const int mod=1e9+7; int t,n,ans; int mul(int x,int n,int mod){ int ans=mod!=1; for(x%=mod;n;n>>=1,x=x*x%mod) if(n&1) ans=ans*x%mod; return ans; } int phi(int n){ //求欧拉函数 int ans=n; for(int i=2;i<=sqrt(n);i++){ if(n%i!=0) continue; ans=ans/i*(i-1); while(n%i==0) n/=i; } if(n>1) ans=ans/n*(n-1); return ans%mod; } signed main(){ scanf("%lld",&t); while(t--){ scanf("%lld",&n),ans=0; for(int i=1;i<=sqrt(n);i++){ if(n%i!=0) continue; ans=(ans+mul(n,i,mod)*phi(n/i)%mod)%mod; if(i*i!=n) ans=(ans+mul(n,n/i,mod)*phi(i)%mod)%mod; //套式子 } printf("%lld\n",ans*mul(n,mod-2,mod)%mod); } return 0; }
4. UVA10601 Cubes
题目大意:给正方体的 12 条棱染色,有 6 种颜色,每种颜色规定数目,求本质不同的方案数(旋转后相同的方案算同一种)。多组数据,1≤t≤60。
Solution:
一个正方体共有 24 种旋转。根据这些不同的旋转方法,构造对应的关于边的置换群。
-
不旋转:1 种,分解为 12 个 1 阶循环。
-
以一对相对面的中心为轴:
-
旋转 90∘:6 种,分解为 3 个 4 阶循环。
-
旋转 180∘:3 种,分解为 6 个 2 阶循环。
-
-
以一对相对棱的中心为轴:
- 旋转 180∘:6 种,分解为 2 个 1 阶循环和 5 个 2 阶循环。
-
以一对对顶点为轴:
- 旋转 120∘:8 种,分解为 4 个 3 阶循环。
因为染色存在数目限制,所以不能直接使用 Pólya 定理。与前面的「Luogu P1446 [HNOI2008]Cards」类似,要把置换的每个循环节都染上相同的颜色。
然后你可以写 组合数 或 六维背包。
五、习题
- Luogu P2561 [AHOI2002]黑白瓷砖(Code)
- SP419 Transposing is Fun
- SP422 Transposing is Even More Fun
- BZOJ 1478 Sgu282 Isomorphism
顺便说说,这篇文章写得挺烂的,还是看 这里 吧 QAQ
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步