群论学习笔记

本文有着大量的感性理解,或没有证明的性质。鄙人尚菜,还请各位看官多多包涵。


一、置换

实际上可以理解为对集合的每个元素一个新的标号,满足这个标号集合与原集合一一对应。

如集合 \(X=\{x_1,x_2,\dots,x_n\}\),他的置换就可以表示为:\(\sigma=(_{x_{p_1}\ \ \ x_{p_2}\ \ \ \dots\ \ \ x_{p_n}}^{\ x_1\ \ \ \ \ x_2\ \ \ \dots\ \ \ \ x_n})\)

做题时通常满足 \(x_i=i\),我们就可以表示为:\(\sigma=x_{p_1}x_{p_2}\dots x_{p_n}\)

明显会出现循环节,比如 \(\sigma=265431\),此时 \(x_1,x_2,x_6\) 就是一个循环。根据这个特性,我们还可以将置换表示为:\(\sigma=(126)(35)(4)\),单独的循环节可以不写,即可以省略为:\(\sigma=(126)(35)\)

运用:\([POI2009]\ PLO\)

考虑可以将交换转化为一个循环节内部的交换,设循环内 \(\sum w_i\)\(sum\)\(\min w_i\)\(min\),循环节的大小为 \(num\),则容易想到一种 \(sum+(num-2)\times min\) 的方案,即从最小值开始交换。

但考虑假如我们先减小循环节最小值,即将全局最小值 \(minn\)\(min\) 交换,再进行上述操作,最后再把 \(min\) 换回来,这样就可以达到 \(sum+min+(num+1)\times minn\) 的代价,两者取 \(\min\) 即可。

时间复杂度 \(O(n)\),其本质等价于置换的循环节式写法。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+5;
int n,ans,minn=2e9,fl[N];
int yc[N],a[N],b[N],w[N];
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>w[i],ans+=w[i];
		minn=min(minn,w[i]);
	}for(int i=1;i<=n;i++)
		cin>>a[i],yc[a[i]]=i;
	for(int i=1;i<=n;i++) cin>>b[i];
	for(int i=1;i<=n;i++){
		if(fl[i]) continue;
		int mn=2e9,nm=1,nw=i;
		while(!fl[nw]){
			mn=min(mn,w[a[nw]]),nm++;
			fl[nw]=1,nw=yc[b[nw]];
		}ans+=min((nm-3)*mn,nm*minn+mn);
	}cout<<ans;
	return 0;
} 

置换的运算就是一个置换叠加上另一个置换,如:

\[(_{1\ \ \ 3\ \ \ 2\ \ \ 4}^{1\ \ \ 2\ \ \ 3\ \ \ 4})(_{2\ \ \ 4\ \ \ 1\ \ \ 3}^{1\ \ \ 2\ \ \ 3\ \ \ 4})=(_{1\ \ \ 3\ \ \ 2\ \ \ 4}^{1\ \ \ 2\ \ \ 3\ \ \ 4})(_{2\ \ \ 1\ \ \ 4\ \ \ 3}^{1\ \ \ 3\ \ \ 2\ \ \ 4})=(_{2\ \ \ 1\ \ \ 4\ \ \ 3}^{1\ \ \ 2\ \ \ 3\ \ \ 4}) \]

有这样一个性质:

长度为奇数的循环节平方时不变,长度为偶数的循环节平方时分成长度相等的两段循环节。

手模容易,不再过多赘述。

二、群

群的定义如下:

给出一个集合 \(G=\{a,b,c,\dots\}\) 和集合 \(G\) 上的二元运算 \(*\),并满足:

  1. \(\forall a,b\in G,\exists c\in G,a*b=c\)
  2. \(\forall a,b,c\in G,(a*b)*c=a*(b*c)\)
  3. \(\exists e\in G,\forall a\in G,a*e=e*a=a\)
  4. \(\forall a\in G,\exists b\in G,a*b=b*a=e,\)\(b=a^{-1}\)

那么称 \((G,*)\) 为群。
摘自信息学奥赛之数学一本通(\(C++\) 版),有改动。

群的运算如下:

对于 \(g\in G\)\(G\) 的子集 \(H,K\),定义 \(g*H=\{gh|h\in H\}\),简写为 \(gH\);定义 \(H*g=\{hg|h\in H\}\),简写为 \(Hg\);定义 \(H*K=\{hk|h\in H,k\in K\}\),简写为 \(HK\);定义 \(H^{-1}=\{h^{-1}|h\in H\}\)
摘自信息学奥赛之数学一本通(\(C++\) 版),有改动。

群的部分定理如下:

定理 \(1\):若 \((G,*)\) 为群,则 \(\forall g\in G,gG=Gg=G\)
定理 \(2\):若 \((G,*)\) 为群,\(H\)\(G\) 的非空子集,并且 \((H,*)\) 为群,则 \(H\)\(G\) 的子群。
定理 \(2\) 扩展:\(HH=H\)\(H^{-1}=H\) 等价于 \(H\)\(G\) 的子群。
摘自信息学奥赛之数学一本通(\(C++\) 版),有改动。

三、置换群

置换群的元素就是置换,运算就是置换的运算,容易证明置换群满足群的四个条件。

四、\(Burnside\) 引理

\(D(a_j)\) 表示在置换 \(a_j\) 中位置没有发生改变的元素的个数,\(L\) 表示本质不同的方案数,则有:

\[L=\frac{1}{|G|}\sum\limits_{j=1}^{s}D(a_j) \]

运用:\([HNOI2008]\ Cards\)

考虑构造置换群。

首先他给你了 \(m\) 个置换,他们满足结合律,但是没有单位元。置换群中的单位元容易想到 \(\sigma=123\dots\),插入他不会对答案产生影响。所以我们构造了一个大小为 \(m+1\) 的置换群。

容易发现一个置换中的一个循环节中的所有位置都必须得是同色,所以直接上三维 \(01\) 背包计算每个置换的 \(D(a_j)\) ,再利用引理计算 \(L\) 即可。时间复杂度 \(O(nmS_rS_bS_g)\)

#include<bits/stdc++.h>
using namespace std;
const int N=65,M=25;
int n,m,p,rn,bn,gn,ps[N];
int fl[N],f[M][M][M];
int qpow(int x,int y){
	int re=1;
	while(y){
		if(y&1) re=re*x%p;
		x=x*x%p,y>>=1;
	}return re;
}void dp(int sz){
	for(int i=rn;~i;i--)
		for(int j=bn;~j;j--)
			for(int k=gn;~k;k--){
				if(i>=sz) f[i][j][k]+=f[i-sz][j][k];
				if(j>=sz) f[i][j][k]+=f[i][j-sz][k];
				if(k>=sz) f[i][j][k]+=f[i][j][k-sz];
				f[i][j][k]%=p;
			}
}int get_dp(){
	for(int i=1;i<=n;i++) fl[i]=0;
	for(int i=0;i<=rn;i++)
		for(int j=0;j<=bn;j++)
			for(int k=0;k<=gn;k++)
				f[i][j][k]=0;
	f[0][0][0]=1;
	for(int i=1;i<=n;i++){
		if(fl[i]) continue;
		int nw=i,sum=0;
		while(!fl[nw])
			sum++,fl[nw]=1,nw=ps[nw];
		dp(sum);
	}return f[rn][bn][gn];
}int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>rn>>bn>>gn>>m;
	n=rn+bn+gn,cin>>p;
	for(int i=1;i<=n;i++) ps[i]=i;
	int sum=get_dp();
	for(int i=1;i<=m;i++){
		for(int j=1;j<=n;j++) cin>>ps[j];
		sum=(sum+get_dp())%p;
	}cout<<sum*qpow(m+1,p-2)%p;
	return 0;
}

该引理通常和 \(dp\) 紧密相连,其原因在于其他办法难以快速枚举 \(D(a_j)\)

五、\(Polya\) 定理

我们可以将置换的循环节式写法(为方便描述,使用第一框题中的例子) \(\sigma=(126)(35)(4)\) 看作多个互不相交的置换的乘积,即 \(\sigma=\sigma_1\sigma_2\sigma_3(\) 其中 \(\sigma_1=(_{1\ 2\ 6}^{2\ 6\ 1}),\sigma_2=(_{3\ 5}^{5\ 3}),\sigma_3=(_{4}^{4}))\)

定理内容如下:

\(G\) 是包含 \(p\) 个元素的置换群,用 \(m\) 种颜色给 \(p\) 个元素染色。定义 \(c(g)\) 表示置换 \(g\) 的循环节个数,则方案数为:

\[L=\frac{1}{|G|}\sum\limits_{i=1}^pm^{c(g_i)} \]

通常该定理和欧拉函数结合使用,当然,容易发现 \(m^{c(g_i)}=D(g_i)\),因此可以理解为是一种在特定情况下快速求解 \(D(a_j)\) 的方法。

运用:\([POJ2154]\ Color\)

显然板子了,问题转化为求解 \(\sum\limits_{i=1}^pn^{c(g_i)}\)。由于 \(n\) 过大,同时 \(c(g_i)\) 的可能值很少,所以考虑枚举 \(c(g_i)\)

考虑到循环节显然等长,长度是 \(len=\gcd(i,n)\)。那么显然有 \(\gcd(\frac{i}{len},\frac{n}{len})=1\),所以长度为 \(len\) 的循环节个数显然有 \(\varphi(\frac{n}{len})\) 个。

根据上述性质,容易发现答案为:

\[\sum_{i|n}\varphi(i)n^{\frac{n}{i}-1} \]

时间复杂度 \(O(t\sqrt n\log n)\)

\(ps.\) 之所以 \(\frac ni\)\(-1\),是因为本身是要整体 \(\times \frac 1n\)

//该代码不能在 POJ 上 AC,但是有正确性
#include<bits/stdc++.h>
#define int long long
using namespace std;
int t,n,p,ans;
int phi(int x){
	int re=x,c=x;
	for(int i=2;i*i<=x;i++){
		if(c%i) continue;
		re=re/i*(i-1);
		while(c%i==0) c/=i;
		if(c==1) return re;
	}if(c==1) return re;
	return re/c*(c-1);
}int qpow(int x,int y){
	int re=1;
	while(y){
		if(y&1) re=re*x%p;
		x=x*x%p,y>>=1;
	}return re;
}void add(int x){
	ans=(ans+phi(x)*qpow(n%p,n/x-1))%p;
}void solve(){
	cin>>n>>p,ans=0;
	for(int i=1;i*i<=n;i++){
		if(n%i) continue;add(i);
		(i*i!=n?add(n/i):void());
	}cout<<ans<<"\n";
}signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>t;
	while(t--) solve();
	return 0;
}
posted @   长安一片月_22  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示