群论基本知识及一些重要定理
群论
一.基本定义
群:给定一个集合{a,b,c...}和集合上的二元运算,要求满足下面四个条件
①.封闭性:对于任意,一定存在,使得
②.结合律:对于任意,有
③.单位元:存在,使得对任意,有
④.逆元:对任意,均存在,使得,其中称作的逆元,记作
如果一个集合满足这个条件,那么就称这个集合是在运算下的一个群
子群:设是一个群,是的一个子集,且在相同意义下仍然构成一个群,那么称是的一个子群
接下来将运算简记为
二.基本性质:
①.一个群的单位元是唯一的
②.群中任意元素的逆元是唯一的
③.对,若,则
④.
(这里做一个说明:群的定义及性质中均没有要求交换律,因此不要想当然地在群运算中进行交换操作!)
三.置换群:
(接下来的内容有个人理解成分在内,如果有不准确的部分请及时指出,谢谢!)
1.置换的定义:
记一个序列{}={}是1~n的一个排列
定义一个置换
其含义是用取代原来的元素,用取代原来的元素...用取代原来的元素
置换的运算定义如下:
设两个元素,,则运算过程如下:
同理可以看出:如果我们计算,则得到的结果应当是
2.置换群的定义:
那么定义置换群{}
不难发现,n个元素的一个置换与1~n的一个排列相对应,因此由1~n的全排列所对应的个置换可以构成一个群,记作,称为n个文字的对称群()
3.循环的定义:
但是我们发现,每次写一个置换太复杂了,因此我们给出一个简单记法:
记
稍微解释一下:原本的一个置换可以写作,那么我们可以把这个置换群写成这个形式:
也就是说我们直接把一个置换连续相接,就能得出一个循环,这样得出的循环就是上面那个形式
但是,一个循环中不一定会出现所有n个元素,而且一个置换可能需要由大量这种循环来构成
举个例子:{}
可以发现,每个元素不一定会出现在每个循环之中,原因是如果一个元素满足,那么这个元素就不必(也无法)写入循环了
而且,如果对于每个都有,那么肯定不能全都省略,因此对于这种由多个循环组成的置换我们一般把它写成一个循环乘积的形式。
若一个循环的元素个数为,我们称这个循环为k阶循环
4.一个置换的循环表示方法:
那么对于任意,我们均可以把写成若干互不相交的循环乘积形式,即:
其中满足
设所有这些循环中阶循环出现的次数为,那么我们记作
所以一个置换可分解成的格式是
显然有一个表达式:
5.共轭类:
在中有相同格式的置换全体,称作与该格式相对应的共轭类
定理:中属于格式的共轭类的元素个数为:
6.k不动置换类:
设是的一个子群,设,中使k不动的置换全体,称作中使k不变的置换类,简称k不动置换类,记作
不难看出,是中所有含有“因子”的置换全体
7.等价类:
给出一个置换群是的一个子群,设,且存在置换,使得在置换p的作用下能够将变为,则称,属于同一个等价类,因此1~n的所有整数可以按照的置换分成若干个等价类,一个数所属的等价类记作
定理:对任意,有:
四.burnside引理:
内容:设是1~n上的一个置换群,则在n上引出的等价类的数量为
人话:一个置换群中共有个置换,每个置换都有一个不动点数量,那么的等价类数量为所有不动点数量的平均值
可能你并不是很懂,我们举个例子:
一个正方形均分成四个格子,用两种颜色对这四个格子进行染色,经过旋转可以重合的两种染色方案记作同一种方案,问有多少种不同的方案?
首先我们可以看到,不考虑任何限制,一共有16种染色方案:
这是初始状态,接下来我们进行计算:
我们认为一个置换是将一个状态通过旋转一定角度获得另一种状态,那么我们可以得到一个置换群
那么最后的答案就是这个置换群的不同等价类个数
直接套用burnside引理可得:
五.Polya定理:
内容:设是n上的一个置换群,用m种颜色涂染这n个对象,其中如果两种方案可以在群作用下互相转化,则称这两种方案为同一种方案,那么总方案数的表达式为:
其中表示置换的循环个数
我们仍然用上面正方形染色的例子,但这次先对每个格子进行编号:
这个正方形的置换一共有四种:
分别对应不旋转,顺时针旋转,旋转180和逆时针旋转
那么可推知
所以最后的染色方案数为
单纯从这个角度讲,burnside引理和polya定理处理的问题其实是一样的
但是仅仅在如此小规模的问题中,两者的效率差异已经体现得非常明显了:burnside引理需要求出每一种染色方案,一共需要找16种,然后在对这些方案之间进行置换,而polya定理仅需要找出原图中的四种置换即可
因此polya的效率相对更高一些
六.例题:
luogu 4980
首先是polya定理没错啦
接下来考虑怎么做
按照套路,首先我们应该找出一个置换群
不难发现,这个置换群的大小应该是n,因为一个长度为n的圆周一共有n种旋转状态
接下来,我们考虑每个置换的循环个数
通过打表严谨的推理可知,第个置换的循环节个数应该为
于是我们立刻可以写出表达式:
然而这个东西求一次就是的,很显然会T飞
所以我们必须处理一下这个问题...
你需要莫比乌斯反演
我们调整一下,可以看到,原表达式等价于下面这个形式:
这样你是不是已经很熟悉了?
再变个形,就是这样:
答案不就呼之欲出了?后面那个不就是欧拉函数的定义嘛
于是我们立刻可以写出最后的表达式
这就是答案!
(话说为什么要在群论里出现莫比乌斯反演啊喂)
可能你没有看出优化在哪里,给出一点解释:枚举一个数的约数复杂度是级别的,而求解则可以一部分预处理,另一部分较大的在线计算,我选择预处理前的,然后剩下的部分暴力计算,考虑到n的范围小于等于,因此暴力计算的部分是很有限的,总复杂度
贴代码:
#include <cstdio> #define ll long long using namespace std; const ll mode=1000000007; ll phi[10000005]; ll pri[10000005]; bool used[10000005]; int cnt=0; int T; ll n; ll pow_mul(ll x,ll y) { ll ans=1; while(y) { if(y&1)ans=ans*x%mode; x=x*x%mode,y>>=1; } return ans; } void init() { phi[1]=1; for(ll i=2;i<=10000000;i++) { if(!used[i])pri[++cnt]=i,phi[i]=i-1; for(ll j=1;j<=cnt&&i*pri[j]<=10000000;j++) { used[i*pri[j]]=1; if(i%pri[j]==0) { phi[i*pri[j]]=phi[i]*pri[j]; break; } phi[i*pri[j]]=phi[i]*(pri[j]-1); } } } ll get_phi(ll x) { if(x<=10000000)return phi[x]; ll ret=x; for(ll i=2;i*i<=x;i++) { if(x%i==0) { ret/=i; ret*=(i-1); while(x%i==0)x/=i; } } if(x!=1)ret/=x,ret*=(x-1); return ret; } void get_ans(ll x) { ll sum=0; for(ll i=1;i*i<=x;i++) { if(x%i==0) { sum=(sum+pow_mul(x,i-1)*get_phi(x/(ll)i)%mode)%mode; ll di=x/i; if(di!=i)sum=(sum+pow_mul(x,di-1)*get_phi(i)%mode)%mode; } } printf("%lld\n",sum); } int main() { init(); scanf("%d",&T); while(T--) { scanf("%lld",&n); get_ans(n); } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY