组合数学——群论学习总结

这个算是组合数学里面比较难理解的,其实在竞赛中用的不少,许多思想都很重要。

限于本人水平有限,下面很多的东西都是来自网上和其他的一些好的博客和视频。

目录

基础概念

重要概念

重要定理

经典习题



基础概念

首先给出一些基本的群论方面的概念(重点不在这里,这里给出主要为了方便下面的学习)。

在数学中,表示一个拥有满足封闭性、满足结合律、有单位元、有逆元二元运算代数结构(百度百科)

是不是很懵,我们先来看一个例子。

G = { 1 , -1 } 在普通乘法作用下是一个群(即(G,*)为一个群),为什么呢?下面一一验证。

首先它满足封闭性:因为不论这两个数怎么乘,最后的答案还在这个集合里面。

结合律:    1 * (-1) * 1 * (-1)  = (-1) * ( 1 * 1 ) = -1。

单位元: 1。

逆元素: 1的逆元为1,-1的逆元为-1。

下面再来解释一下这几个性质。

封闭性: 即该集合的任意两个元素在该运算符的作用下得到的结果还在这个集合里面。

结合律:

\forall a,b,c\in G, \left ( a\cdot b\right )\cdot c= a\cdot \left ( b\cdot c \right ) ( . 即表示该运算符)

单位元:

G中任意一个元素乘于该元素的结果都是本身。

\exists e\in G,\forall a\in G, e\cdot a=a\cdot e=a

逆元:G中的任意一个元素都有一个逆元

\forall a\in G,\exists b\in G,a\cdot b= b\cdot a= e,则b称为a的逆元,记做a^{-1}

(结合上面的例子大家大概能理解这个东西吧!!!都是概念性的QWQ)

群元素的个数有限,称为有限群,且其中元素的个数称为阶,记为|G|,群元素的个数无限,称为无限群。

若对于群元素中的任意两个元素a,b都有ab=ba那么称G为交换群,简称Abel群。


重要概念

下面就是最关键的置换群,也是用的最多的(转自https://blog.csdn.net/xuzengqiang/article/details/7476671)。

置换:简单来说就是对元素进行重排列,如下图所示。置换是[1,n]到[1,n]的一一映射。
例:设X={1,2,3,4....n},设π是X的一个变换,满足π:1->a1,2->a2,......n->an,其中a1,a2...an是X的一个排列,则称π是X上的一个置换。
可将π记为

置换

同一置换用这样的表示法有n!种,但其对应的关系不变。
假设循环π只这样一个置换,满足π:a1->a2,a2->a3,.............ak->a1,但是对于其他元素保持不变,即:a->a,
可将π记为

 \begin{pmatrix} a1& a2 &a3\cdots ak\\ a2& a3 &a4\cdots a1 \end{pmatrix}
称为k阶循环,k为循环长度。
每个置换都可以写成若干个互不相交的循环的乘积,且表示是唯一的.

  \begin{pmatrix} 1 &2&3&4&5&6 \\ 2&4&5&1&3&6 \end{pmatrix}

    则可以表示为(124)(35)(6),置换的循环节数是上面的循环个数,上面的例题的循环节数为3.
=============================================================================================
定义:设G是有限集X上的置换群,点a,b∈X称为"等价"的,当且仅当,存在π∈G使得π(a)=b,记为a~b,这种等价条件下,X的元素形成的等价类称为G的轨道,它是集X的一个子集,G的任意两个不同的轨道之交是空集,所以置换群G的轨道全体是集合X的一个划分,构成若干个等价类,等价类的个数记为L。
Zk (K不动置换类):设G是1…n的置换群。若K是1…n中某个元素,G中使K保持不变的置换的全体,记以Zk,叫做G中使K保持不动的置换类,简称K不动置换类。
Ek(等价类):设G是1…n的置换群。若K是1…n中某个元素,K在G作用下的轨迹,记作Ek。即K在G的作用下所能变化成的所有元素的集合。.
这个时候有:|Ek|*|Zk|=|G|成立(k=1,2,.....n)
C(π):对于一个置换π∈G,及a∈X,若π(a)=a,则称a为π的不动点。π的不动点的全体记为C(π)。例如π=(123)(3)(45)(6)(7),X={1,2,3,4,5,6,7};那么C(π)={3,6,7}共3个元素。


重要定理

下面讲到了用的最多的polya定理和burnside定理。
Burnside引理:L=1/|G|*(Z1+Z2+Z3+Z4+......Zk)=1/|G|*(C(π1)+C(π2)+C(π3)+.....+C(πn))(其中k∈X,π∈G)

简单点而言就是

对于一个置换f,若一个染色方案s经过置换后不变,称s为f的不动点。将f的不动点数目记为C(f),则可以证明等价类数目为所有C(f)的平均值.

举个例子说明一下。(来自https://blog.csdn.net/liangzhaoyang1/article/details/72639208)

一正方形分成4格,2着色,有多少种方案?其中,经过转动相同的图象算同一方案。 

对于每种格子我们都有两种选择,所以会有一下16种方案: (百度百科) 
 这里写图片描述
但是对于这16种方案可以归一下类:  
Θ不动:a1=(1)(2)…(16)  
Θ逆时针转90度 :a2=(1)(2)(3 4 5 6)(7 8 9 10) (11 12)(13 14 15 16)  
Θ顺时针转90度 :a3=(1)(2)(6 5 4 3)(10 9 8 7)(11 12)(16 15 14 13)  
Θ转180度:a4=(1)(2)(3 5)(4 6)(7 9)(8 10)(11)(12) (13 15)(14 16)  
(a,b,c)表示a,b,c可以通过旋转得到。 
由Burnside引理,共有(16+2+2+4)/4=6(种方案). 
burnside是一种计数方法,用来计算含有不等价类的数量  
burnside算法的关键是找好“置换群”。 

利用Burnside引理要首先列出所有nm种可能的染色方案,然后找出在每个置换下保持不变的方案数。显然当m或nn很大的时候,这个方法会非常繁琐。 这时就需要用到polya定理.


Polya定理:设G={π1,π2,π3........πn}是X={a1,a2,a3.......an}上一个置换群,用m中颜色对X中的元素进行涂色,那么不同的涂色方案数为:1/|G|*(mC(π1)+mC(π2)+mC(π3)+...+mC(πk)). 其中C(πk)为置换πk的循环节的个数。

假设一个置换有k个循环,易知每个循环对应的所有位置颜色需一致,而任意两个循环之间选什么颜色互不影响。因此,如果有m种可选颜色,则该置换对应的不动点个数为m^{k}。用其替换burnside引理中的C(f),即C(f)=m^{k}。得到等价类数目为:

\frac{\sum_{i=0}^{|F|}m^{k_{i}}}{|F|}

 

其中|F|表示置换的数目,ki表示第i个置换包含的循环个数。

 这里写图片描述
还是这个例子吧。

Θ不动:a1=(1)(2)(3)(4)  
Θ旋转90度 :a2=(1 2 3 4)  
Θ旋转180度 :a3=(1 3)(2 4)  
Θ旋转270度:a4=(1 4 3 2)  
比如,“逆时针旋转180度”,这个置换写成循环的乘积就是(1,3)(2,4),即1和3互变,2和4互变,不难发现,1和3的颜色必须相同,2和4的颜色也必须相同,而1-3和2-4的颜色互不相干。 
由Polya定理得,共有\frac{\left ( 2^{4}+2^{1}+2^{2}+2^{1} \right )}{4}=6种方案). 
可以看Burnside引理和Polya定理是一样的,Polya定理是Burnside引理的优化。
 


经典习题

终于把概念说完了,花了将近一节实验!

下面给出一些经典和好的习题

发一个我自己在vjudge上拉的群论专题训练:https://vjudge.net/contest/332708

其实主要分了三个部分:置换(循环节),burnside定理和polya定理(其实还有一道原根的题,就是懒省事)。

再发一个答案详解的链接:https://blog.csdn.net/WhereIsHeroFrom/article/details/79631703

我自己拉的专题里面有一些题目是来自这个博客,不过里面有一些错误,估计也是作者的笔误,我还是建议不会的话对单独的一个题百度搜索,找到一个自己喜欢的讲解详细的代码进行理解。

下面抛几道我觉得很不错的题:

详解请参考上面发的博客链接。

poj2888

一道综合知识运用的好题,需要用到的知识:矩阵快速幂,burnside定理,快速幂,逆元,质数线性筛,求欧拉函数值以及对公式的化简。

这个题还有一个升级版本,需要利用矩阵来求出递推式(hdu2865)

下面抛代码(其实我是为了补之前写的题的博客QAQ)

#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <queue>
#define rp(i, s, t) for (i = (s); i <= (t); i++)
#define RP(i, s, t) for (i = (t); i >= (s); i--)
#define ll long long
#define ull unsigned long long
using namespace std;
inline int read()
{
    int a = 0, b = 1;
    char c = getchar();
    while (c < '0' || c > '9')
    {
        if (c == '-')
            b = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9')
    {
        a = (a << 3) + (a << 1) + c - '0';
        c = getchar();
    }
    return a * b;
}
inline void write(int n)
{
    if (n < 0)
    {
        putchar('-');
        n = -n;
    }
    if (n >= 10)
        write(n / 10);
    putchar(n % 10 + '0');
}
struct Matrix
{
    int mat[12][12];
} G;//矩阵
const int p = 9973;
int n, m, k;
const int N = 1e5 + 7;
int visited[N], prime[N];
int tot;
void init() //预处理求出一部分的质数(线性筛)
{
    memset(visited, 0, sizeof visited);
    tot = 0;
    int i;
    rp(i, 2, N - 1)
    {
        if (!visited[i])
        {
            prime[tot++] = i;
            for (int j = i + i; j < N; j += i)
                visited[j] = 1;
        }
    }
}
ll phi(ll n) //求n得欧拉函数的值
{
    ll res = n;
    for (int i = 0; prime[i] * prime[i] <= n; i++)
    {
        if (n % prime[i] == 0)
        {
            res = res - res / prime[i];
            while (n % prime[i] == 0)
                n /= prime[i];
        }
    }
    if (n > 1)
        res = res - res / n;
    return res % p;
}
ll quick_mod(ll a, ll b) //快速幂
{
    ll res = 1;
    while (b)
    {
        if (b & 1)
            res = (res * a) % p;
        a = (a * a) % p;
        b >>= 1;
    }
    return res % p;
}
Matrix mult(Matrix a,Matrix b){
    Matrix c;
    memset(c.mat,0,sizeof c.mat);
    int i,j,k;
    rp(i,0,m-1){
        rp(k,0,m-1){
            if(a.mat[i][k]){
                rp(j,0,m-1){
                    c.mat[i][j]=(c.mat[i][j]+a.mat[i][k]*b.mat[k][j])%p;
                }
            }
        }
    }
    return c;
}
Matrix mat_pow(Matrix a,int b){
    int i;
    Matrix res;
    memset(res.mat,0,sizeof res.mat);
    rp(i,0,m-1) res.mat[i][i]=1;
    while(b){
        if(b&1) res=mult(res,a);
        a=mult(a,a);
        b>>=1;
    }
    return res;
}
ll solve(int len)
{
    ll res = 0;
    int i;
    Matrix matrix = mat_pow(G, len);
    rp(i, 0, m - 1)
        res += matrix.mat[i][i];
    return res % p;
}
void burnside()
{
    ll ans = 0;
    for (int i = 1; i * i <= n; i++)
    {
        if (n % i == 0)
        {
            if (i * i == n)
                ans = (ans + solve(i) * phi(i)) % p;
            else
                ans = (ans + solve(n / i) * phi(i) + solve(i) * phi(n / i)) % p;
        }
    }
    ans = (ans * quick_mod(n, p - 2)) % p;//乘上n的逆元
    write(ans);
    printf("\n");
}
int main()
{
    int T = read(), i, j;
    init();
    while (T--)
    {
        n = read(), m = read(), k = read();
        rp(i, 0, m - 1)
            rp(j, 0, m - 1)
                G.mat[i][j] = 1;
        while (k--)
        {
            int u = read(), v = read();
            G.mat[u - 1][v - 1] = G.mat[v - 1][u - 1] = 0;
        }
        burnside();
    }
    return 0;
}

还有一道burnside定理+多重集排列的综合题,也是很不错的题,思想很好(UVA11255)。

就不解释题目和答案了,看代码理解是每一个acm的必经之路。。。。。

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<queue>
#define rp(i,s,t) for (i = (s); i <= (t); i++)
#define RP(i,s,t) for (i = (t); i >= (s); i--)
#define ll long long
#define ull unsigned long long
using namespace std;
inline int read()
{
    int a=0,b=1;
    char c=getchar();
    while(c<'0'||c>'9')
    {
        if(c=='-')
            b=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')
    {
        a=(a<<3)+(a<<1)+c-'0';
        c=getchar();
    }
    return a*b;
}
inline void write(int n)
{
    if(n<0)
    {
        putchar('-');
        n=-n;
    }
    if(n>=10)
        write(n/10);
    putchar(n%10+'0');
}
int a[3],sum;
ll C[105][105];
ll gcd(ll a,ll b){
    return b==0?a:gcd(b,a%b);
}
void init()
{
    memset(C,0,sizeof(C));
    int i,j;
    C[1][1]=C[0][0]=1;
    rp(i,1,104) C[i][0]=1;
    rp(i,2,104)
        rp(j,1,i)
            C[i][j]=C[i-1][j]+C[i-1][j-1];
}
ll solve(int k)
{
    int b[3],s=0,i;
    rp(i,0,2){
        if(a[i]%k!=0) return 0;
        
        s+=a[i]/k;
        b[i]=a[i]/k;
    }
    ll ans=1;
    rp(i,0,2){
        ans*=C[s][b[i]];
        s-=b[i];
    }
    return ans;
}
void burnside()
{
    sum=a[0]+a[1]+a[2];
    ll ans=0;
    int i,j;
    rp(i,1,sum) ans+=solve(sum/gcd(sum,i));
    if(sum&1){
        rp(i,0,2){
            if(a[i]<1) continue;
            a[i]-=1;
            ans+=sum*solve(2);
            a[i]+=1;
        }
    }
    else{
        rp(i,0,2){
            if(a[i]<1) continue;
            a[i]-=1;
            rp(j,0,2){
                if(a[j]<1) continue;
                a[j]-=1;
                ans+=(sum/2)*solve(2);
                a[j]+=1;
            }
            a[i]+=1;
        }
        ans+=(sum/2)*solve(2);
    }
    printf("%lld\n",ans/(2*sum));
}
int main(){
	int T=read(),i;
    init();
    while(T--){
        rp(i,0,2) a[i]=read();
        burnside();
    }
	return 0;
}

 

 

 

 

 

 

 

 

 

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值