前言
群论这个东西在OI中不是很常用,然而一旦考到就是极拉分的题,所以为了不被卡脖子,才水了这么一篇垃圾文章。
群的定义
正式地说,群是一种代数系统。代数系统大概就是由运算对象和运算方法组成的一个东西。
具体来讲,一个群 < G , op > <G,\text{op}> <G,op> 由两部分组成:
-
op
\text{op}
op:一种满足结合律的二元运算。在研究时,我们一般用乘法的表示方式来表示所有这种运算,如
a
op
b
a\,\text{op}\,b
aopb 写为
a
b
ab
ab,
a
op
a
op
.
.
.
op
a
(
k
个
a
)
a\,\text{op}\,a\,\text{op}...\text{op}\,a(k个a)
aopaop...opa(k个a) 写为
a
k
a^k
ak。
另外,我们还沿用数论中的一些定义,如用 e e e 表示单位元(满足 ∀ a , a e = e a = a \forall a,ae=ea=a ∀a,ae=ea=a,如乘法的单位元为1,加法的单位元为0),用 a − 1 a^{-1} a−1 表示 a a a 的逆元(满足 a a − 1 = a − 1 a = e aa^{-1}=a^{-1}a=e aa−1=a−1a=e,如 a a a 的加法运算逆元为 − a -a −a)。 - G G G:群的主体,一个集合,满足① e ∈ G e\in G e∈G ② ∀ a ∈ G , a k ∈ G ( k ∈ Z ) \forall a\in G,a^k\in G(k\in \mathbb{Z}) ∀a∈G,ak∈G(k∈Z) ③ ∀ a , b ∈ G , a b ∈ G \forall a,b\in G,ab\in G ∀a,b∈G,ab∈G。通俗地讲,这是个封闭的集合。
特别地,如果运算 op \text{op} op 满足交换律,则称这个群为 a b e l abel abel 群。
当确定或默认了运算之后,我们一般就只用集合来表示群,比如我们会说“某某集合是一个群”,就好比群只是一种特殊的集合。
还有一些定义,如群的阶、元素的阶等,都是沿用整数数论中的定义,并且不用深入了解。
子群
定义很简单: H H H 为 G G G 的子群,当且仅当 H H H 是 G G G 的非空子集,且 H H H 是一个群。
从一个元素可以构造一些特殊的子群。设元素 a a a 属于群 G G G:
- 定义 < a > = { a k ∣ k ∈ Z } <a>=\{a^k|k\in\mathbb{Z}\} <a>={ak∣k∈Z},容易证明 < a > <a> <a> 是 G G G 的一个子群,且是由 a a a 生成的子群。 < a > <a> <a> 是一种特殊的群,叫循环群,其中 a a a 为生成元。
- 定义 N ( a ) = x ∣ x ∈ G 且 x a = a x N(a)={x|x\in G且xa=ax} N(a)=x∣x∈G且xa=ax,也就是所有在运算中可以与 a a a 交换的元素构成的集合,容易证明 N ( a ) N(a) N(a) 是 G G G 的子群,称 N ( a ) N(a) N(a) 为 G G G 的正规化子。
设 H H H 是 G G G 的子群,定义 x H x − 1 = { x h x − 1 ∣ h ∈ H , x ∈ G } xHx^{-1}=\{xhx^{-1}|h\in H,x\in G\} xHx−1={xhx−1∣h∈H,x∈G},则 x H x − 1 xHx^{-1} xHx−1 是 G G G 的子群,称为 H H H 的共轭子群。(定义 b b b 是 a a a 的共轭当且仅当 ∃ x ∈ G , b = x a x − 1 \exists x\in G,b=xax^{-1} ∃x∈G,b=xax−1)
若 H , K H,K H,K 都是 G G G 的子群,则 H ∩ K H∩K H∩K 也是 G G G 的子群。
陪集分解
设 H H H 是 G G G 的子群, a ∈ G a\in G a∈G,定义 H a = { h a ∣ h ∈ H } Ha=\{ha|h\in H\} Ha={ha∣h∈H},称 H a Ha Ha 为 H H H 在 G G G 中的一个右陪集。注意陪集是一个集合,不保证是个群。左陪集和右陪集的定义类似,下面只讨论右陪集。
这里有一个结论: H a Ha Ha 和 H H H 等势。什么意思,就是说 H a Ha Ha 和 H H H 里的值是一一对应的,如果 H H H 是有限群,那么 H a Ha Ha 和 H H H 的大小相等。
L
a
g
r
a
n
g
e
\rm Lagrange
Lagrange(拉格朗日)定理:
对于
∀
a
,
b
∈
G
\forall a,b\in G
∀a,b∈G,显然有
a
∈
H
b
⇔
b
∈
H
a
⇔
H
a
=
H
b
\,a\in Hb \Leftrightarrow b\in Ha \Leftrightarrow Ha=Hb
a∈Hb⇔b∈Ha⇔Ha=Hb,此时
a
,
b
a,b
a,b 可以划分为一个等价类。很明显,
a
a
a 和
H
a
Ha
Ha 里的其它所有元素都可以划分为一个等价类,而
a
a
a 与
∉
H
a
\notin Ha
∈/Ha 的元素都不能划分为一个等价类,所以
a
a
a 所在等价类的大小
=
∣
H
a
∣
=
∣
H
∣
=|Ha|=|H|
=∣Ha∣=∣H∣。
因此我们可以得出:子群
H
H
H 的陪集可以把
G
G
G 划分为若干个等价类,每个等价类的大小都为
∣
H
∣
|H|
∣H∣,那么
∣
H
∣
|H|
∣H∣ 必为
∣
G
∣
|G|
∣G∣ 的因子,等价类数量=
∣
G
∣
∣
H
∣
\frac{|G|}{|H|}
∣H∣∣G∣。
共轭类分解
假设 b = x a x − 1 b=xax^{-1} b=xax−1 是 a a a 的一个共轭,显然 a a a 也是 b b b 的一个共轭,因为 a = x − 1 b x a=x^{-1}bx a=x−1bx。同理容易证明,共轭关系不仅有对称性,自反性,还有传递性。像上面陪集的等价类一样,这里也可以把有共轭关系的元素划分为共轭等价类。
由于 x a x − 1 = y a y − 1 ⇔ a x − 1 y = x − 1 y a ⇔ x − 1 y ∈ N ( a ) ⇔ y ∈ x N ( a ) xax^{-1}=yay^{-1}\Leftrightarrow ax^{-1}y=x^{-1}ya\Leftrightarrow x^{-1}y\in N(a)\Leftrightarrow y\in xN(a) xax−1=yay−1⇔ax−1y=x−1ya⇔x−1y∈N(a)⇔y∈xN(a),就是说, a a a 经过 x x x 和 y y y 能产生相同的共轭,等价于 x , y x,y x,y 属于同一个 N ( a ) N(a) N(a) 的左陪集的等价类。
于是 a a a 的不同的共轭数量,就等于 N ( a ) N(a) N(a) 的左陪集等价类的数量,即 ∣ G ∣ ∣ N ( a ) ∣ \frac{|G|}{|N(a)|} ∣N(a)∣∣G∣。
置换群
要搞懂置换群,首先要知道置换是啥。置换是一种变换。变换是一个定义域和值域相同的函数,可以把一个元素变为另一个同类型的元素,比如把矩阵变为另一个矩阵,而不是变为多项式。
注意变换不要求可逆,因此一个矩阵乘上一个不满秩的矩阵也是变换。
置换则是一种特殊的变换,满足定义域和值域是有穷集合,且定义域和值域中元素一一对应,函数是个双射。容易发现,由于定义域和值域元素一一对应,所以置换是可逆的,或者说有逆元的。
置换群就是由置换构成的群。它的运算是函数的合成,即 f ∗ g ( x ) = g ( f ( x ) ) f*g(x)=g(f(x)) f∗g(x)=g(f(x)),具有结合律。置换群的定义域就是集合里的置换的定义域。
比如定义一种作用在1~n的排列上的置换为向左循环平移,平移的长度有 n n n 种,所以置换群的大小为 n n n,单位元是平移0单位,平移 x x x 单位的逆元是平移 − x ( m o d n ) -x(\bmod n) −x(modn) 单位。
这里有一些关于置换群的重要概念:
设 G G G 是作用在定义域 A A A 上的有穷置换群;
定义 G a = { g ∣ g ∈ G 且 g ( a ) = a } G^a=\{g|g\in G且g(a)=a\} Ga={g∣g∈G且g(a)=a},由于 ① e ( a ) = a e(a)=a e(a)=a,② g ( a ) = a ⇒ g − 1 ( a ) = a g(a)=a\Rightarrow g^{-1}(a)=a g(a)=a⇒g−1(a)=a 且 ③ f ( a ) = a , g ( a ) = a ⇒ f ∗ g ( a ) = a f(a)=a,g(a)=a\Rightarrow f*g(a)=a f(a)=a,g(a)=a⇒f∗g(a)=a,所以 G a G^a Ga 是 G G G 的子群,我们把 G a G^a Ga 称作 a a a 的稳定子群(这话有点奇怪,是“ a a a 的”不是“ G G G 的”,但好像又没毛病)。
定义 G ( a ) = { g ( a ) ∣ g ∈ G } G(a)=\{g(a)|g\in G\} G(a)={g(a)∣g∈G},称 G ( a ) G(a) G(a) 为 a a a 的轨道。显然有 a ∈ G ( a ) a\in G(a) a∈G(a),且若 b ∈ G ( a ) b\in G(a) b∈G(a),则 a ∈ G ( b ) a\in G(b) a∈G(b)。这告诉我们,轨道也是种等价类,定义域 A A A 被划分为若干个轨道,每个元素在且仅在一个轨道上。我们用 L L L 表示轨道数量。
定义 C ( g ) = ∑ a ∈ A [ g ( a ) = a ] C(g)=\sum_{a\in A}[g(a)=a] C(g)=∑a∈A[g(a)=a],表示置换 g g g 的不动点数量。
轨道-稳定子群定理
我们考察一下
a
a
a 的轨道的性质:
f
(
a
)
=
g
(
a
)
⇔
f
∗
g
−
1
(
a
)
=
a
⇔
f
g
−
1
∈
G
a
⇔
f
∈
G
a
g
⇔
G
a
f
=
G
a
g
f(a)=g(a)\Leftrightarrow f*g^{-1}(a)=a\Leftrightarrow fg^{-1}\in G^a\Leftrightarrow f\in G^ag\Leftrightarrow G^af=G^ag
f(a)=g(a)⇔f∗g−1(a)=a⇔fg−1∈Ga⇔f∈Gag⇔Gaf=Gag
这说明,
f
,
g
f,g
f,g 为
a
a
a 的轨道贡献同一个元素,等价于
f
,
g
f,g
f,g 属于
G
a
G^a
Ga 的同一个陪集等价类,所以
a
a
a 的轨道上不同元素个数就等于
G
a
G^a
Ga 的陪集等价类数量。
由此我们得到轨道-稳定子群定理: ∣ G ( a ) ∣ = ∣ G ∣ ∣ G a ∣ ⇒ ∣ G ∣ = ∣ G a ∣ ⋅ ∣ G ( a ) ∣ |G(a)|=\frac{|G|}{|G^a|}\Rightarrow |G|=|G^a|\cdot|G(a)| ∣G(a)∣=∣Ga∣∣G∣⇒∣G∣=∣Ga∣⋅∣G(a)∣。
Burnside引理
久等了
运用上面的所有结论,我们开始推式子吧:
∑
g
∈
G
C
(
g
)
=
∑
g
∈
G
∑
a
∈
A
[
g
(
a
)
=
a
]
=
∑
a
∈
A
∣
G
a
∣
=
∑
a
∈
A
∣
G
∣
∣
G
(
a
)
∣
=
∣
G
∣
∑
a
∈
A
1
∣
G
(
a
)
∣
\sum_{g\in G}C(g)=\sum_{g\in G}\sum_{a\in A}[g(a)=a]\\ =\sum_{a\in A}|G^a|\\=\sum_{a\in A}\frac{|G|}{|G(a)|}\\ =|G|\sum_{a\in A}\frac{1}{|G(a)|}
g∈G∑C(g)=g∈G∑a∈A∑[g(a)=a]=a∈A∑∣Ga∣=a∈A∑∣G(a)∣∣G∣=∣G∣a∈A∑∣G(a)∣1
得到
B
u
r
n
s
i
d
e
\rm Burnside
Burnside 引理:
∑
g
∈
G
C
(
g
)
=
∣
G
∣
∑
a
∈
A
1
∣
G
(
a
)
∣
=
∣
G
∣
L
\sum_{g\in G}C(g)=|G|\sum_{a\in A}\frac{1}{|G(a)|}=|G|L
∑g∈GC(g)=∣G∣∑a∈A∣G(a)∣1=∣G∣L。
Pólya 定理
其实就是 B u r n s i d e \rm Burnside Burnside 引理变了个样子: L = ∑ C ( g ) ∣ G ∣ L=\frac{\sum C(g)}{|G|} L=∣G∣∑C(g),但是这个形式却非常实用。
有一类经典问题:把一个 n n n 个点的环染色,每个点可以染 m m m 种颜色中的一种,问有多少种不同的染色方案。
考虑破环成链,对于所有长度为 n n n 的序列,两个序列接回环后相同意味着一个序列可以通过循环移位变成另一个序列。我们用上面提到过的循环平移的置换群来描述的话,就是序列 a , b a,b a,b 会被算入同一个环的方案,等价于 ∃ g ∈ G , g ( a ) = b \exists g\in G,g(a)=b ∃g∈G,g(a)=b,等价于 b ∈ G ( a ) b\in G(a) b∈G(a),等价于 a , b a,b a,b 在同一个轨道上。
所以不同的环排列数就是序列的轨道数量 L L L,我们可以通过求 ∑ C ( g ) ∣ G ∣ \frac{\sum C(g)}{|G|} ∣G∣∑C(g) 来求环排列数。
在这个问题中, ∣ G ∣ = n |G|=n ∣G∣=n, ∑ C ( g ) \sum C(g) ∑C(g) 就是对于 0 n − 1 0~n-1 0 n−1 的 n n n 种置换,求出每种置换下前后不变的序列的数量然后求和。对于平移 k k k 单位的变换,我们把循环距离 k k k 的位置连边,容易发现连通块数量是 gcd ( k , n ) \gcd(k,n) gcd(k,n)(我们把平移0单位看成是平移 n n n 单位),所以 A n s = ∑ i = 1 n m gcd ( i , n ) n Ans=\frac{\sum_{i=1}^nm^{\gcd(i,n)}}{n} Ans=n∑i=1nmgcd(i,n)。
继续化简式子:
∑
i
=
1
n
m
gcd
(
i
,
n
)
=
∑
i
∣
n
m
i
∑
i
∣
j
[
gcd
(
j
,
n
)
=
i
]
=
∑
i
∣
n
m
i
∑
j
=
1
n
i
[
gcd
(
j
,
n
i
)
=
1
]
=
∑
i
∣
n
m
i
φ
(
n
i
)
=
∑
i
∣
n
m
n
i
φ
(
i
)
\sum_{i=1}^nm^{\gcd(i,n)}=\sum_{i|n}m^i\sum_{i|j}[\gcd(j,n)=i]\\ =\sum_{i|n}m^i\sum_{j=1}^{\frac{n}{i}}[\gcd(j,\frac{n}{i})=1]\\ =\sum_{i|n}m^i\varphi(\frac{n}{i})=\sum_{i|n}m^{\frac{n}{i}}\varphi(i)
i=1∑nmgcd(i,n)=i∣n∑mii∣j∑[gcd(j,n)=i]=i∣n∑mij=1∑in[gcd(j,in)=1]=i∣n∑miφ(in)=i∣n∑minφ(i)
因此有一种做法是:先把
n
n
n 质因数分解,然后用深搜枚举质因子指数的方式枚举
n
n
n 的因子,可以均摊
O
(
1
)
O(1)
O(1) 地算出欧拉函数值。这个做法可以在
O
(
n
+
d
(
n
)
log
n
)
O(\sqrt{n}+d(n)\log n)
O(n+d(n)logn) 时间内算出答案,由于
n
n
n 的因数个数
d
(
n
)
d(n)
d(n) 实际上是远小于
n
\sqrt{n}
n 的,所以复杂度差不多就是
O
(
n
)
O(\sqrt{n})
O(n),比那些
O
(
n
)
O(n)
O(n) 以上的DP方法快得多。
例题:[HNOI2008]Cards
这个洗牌法已经非常接近一个置换群了,所以套 Pólya 定理,算每种洗牌法的不动点数量,直接求出连通块然后用三维01背包即可。
这题考察对群的理解:洗牌法集合唯一不同于一个置换群的地方是它有可能没有单位元(比如样例),所以你要手动补上单位元。
附上代码:
#include<bits/stdc++.h>//JZM yyds!!
#define ll long long
#define uns unsigned
#define IF (it->first)
#define IS (it->second)
#define END putchar('\n')
using namespace std;
const int MAXN=100005;
const ll INF=1e18;
inline ll read(){
ll x=0;bool f=1;char s=getchar();
while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}
while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+(s^48),s=getchar();
return f?x:-x;
}
int ptf[50],lpt;
inline void print(ll x,char c='\n'){
if(x<0)putchar('-'),x=-x;
ptf[lpt=1]=x%10;
while(x>9)x/=10,ptf[++lpt]=x%10;
while(lpt)putchar(ptf[lpt--]^48);
if(c>0)putchar(c);
}
inline ll lowbit(ll x){return x&-x;}
inline ll ksm(ll a,ll b,ll mo){
ll res=1;
for(;b;b>>=1,a=a*a%mo)if(b&1)res=res*a%mo;
return res;
}
int n,m,a,b,c,P,dp[21][21][21],w[65],k,ans;
inline void ad(int&a,int b){a+=b;if(a>=P)a-=P;}
int fa[65],fac[65];
inline int finds(int x){
return !fa[x]?x:(fa[x]=finds(fa[x]));
}
signed main()
{
a=read(),b=read(),c=read(),m=read(),P=read();
n=a+b+c;
bool hv=0;
for(int D=1,o=1;D<=m;D++){
for(int i=1;i<=n;i++)fa[i]=0;
bool ok=1;
for(int i=1;i<=n&&o;i++){
int x=read();
if(x^i)ok=0;
if(finds(x)^finds(i))fa[finds(x)]=finds(i);
}
hv|=ok,k=0;
for(int i=1;i<=n;i++)if(finds(i)==i){
w[++k]=0;
for(int j=1;j<=n;j++)if(finds(j)==i)w[k]++;
}
memset(dp,0,sizeof(dp));
dp[0][0][0]=1;
for(int i=1;i<=k;i++)
for(int x=a;~x;x--)
for(int y=b;~y;y--)
for(int z=c;~z;z--){
if(x>=w[i])ad(dp[x][y][z],dp[x-w[i]][y][z]);
if(y>=w[i])ad(dp[x][y][z],dp[x][y-w[i]][z]);
if(z>=w[i])ad(dp[x][y][z],dp[x][y][z-w[i]]);
}
ad(ans,dp[a][b][c]);
if(D==m&&!hv)m++,o=0;
}
ans=ans*ksm(m,P-2,P)%P;
print(ans);
return 0;
}