burnside引理

burnside引理:

对于置换群G,G的轨道数= (G/|G|

没错我也是来报社的,反正我只是打算自己总结一下

那么这个东西有什么用呢?有一类问题是这样的:有N个元素,M种颜色(或者每种颜色有使用次数限制),那么现在还有一个置换群,一种染色方案与它经过这些置换之后得到的方案视为等价,求不同的染色方案数。

首先不考虑在置换群下的等价,把每种染色方案视为一个元素,对于每个染色方案,经过一个置换以后都可以对应另一个染色方案(也可能不变)。那么对于每个置换我们又能得到以染色方案为元素的置换,考虑这些以方案为元素的置换构成的置换群,记为GG。根据轨道的定义(一个轨道内的元素经过置换群内的置换得到的都是同一轨道内的元素),我们要求的方案数正是GG的轨道数,那么就可以利用burnside引理求出轨道数即方案数了。

于是不动点怎么求?这个应该是因题而异的。我刚做的一道题的做法是:对于原来每个置换,使每个循环节内的元素都染上同一种颜色,再求这种情况下的方案数,那就得到这个置换在GG中对应的置换的不动点的个数了,因为很显然这些方案在这个置换下都不会发生改变。于是每个置换求一遍不动点个数再加起来就好啦。至于有没有其他方法就有待研究啦。

其实我刚做的题是BZOJ1004,还是贴一下代码吧→_→

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<string>
#include<iomanip>
#include<vector>
#include<set>
#include<map>
#include<queue>

using namespace std;
typedef long long LL;
typedef unsigned long long ULL;

#define rep(i,k,n) for(int i=(k);i<=(n);i++)
#define rep0(i,n) for(int i=0;i<(n);i++)
#define red(i,k,n) for(int i=(k);i>=(n);i--)
#define sqr(x) ((x)*(x))
#define clr(x,y) memset((x),(y),sizeof(x))
#define pb push_back
int s[5],n,m,mod,cnt,ans,a[110],b[110],dp[25][25][25];
bool vis[110];

int quipow(int x,int k)
{
    int ret=1;
    while(k)
    {
        if(k&1)ret=ret*x%mod;
        x=x*x%mod;
        k>>=1;
    }
    return ret;
}

inline void upd(int &x,int y)
{
    x=(x+y)%mod;
}

int gao()
{
    clr(dp,0);
    dp[0][0][0]=1;
    rep(i,1,cnt)
    {
        red(j,s[1],0)red(k,s[2],0)red(l,s[3],0)
        {
            if(j>=b[i])upd(dp[j][k][l],dp[j-b[i]][k][l]);
            if(k>=b[i])upd(dp[j][k][l],dp[j][k-b[i]][l]);
            if(l>=b[i])upd(dp[j][k][l],dp[j][k][l-b[i]]);
        }
    }
    return dp[s[1]][s[2]][s[3]];
}

int main()
{
    rep(i,1,3)scanf("%d",&s[i]);
    n=s[1]+s[2]+s[3];
    scanf("%d%d",&m,&mod);
    cnt=n;
    rep(i,1,n)b[i]=1;
    ans=gao();
    rep(i,1,m)
    {
        rep(j,1,n)scanf("%d",&a[j]);
        clr(vis,0);
        cnt=0;
        clr(b,0);
        rep(j,1,n)if(!vis[j])
        {
            cnt++;
            int x=j;
            while(1)
            {
                vis[x]=1;
                b[cnt]++;
                if(vis[a[x]])break;
                x=a[x];
            }
        }
        ans=(ans+gao())%mod;
    }
    printf("%d\n",ans*quipow(m+1,mod-2)%mod);

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值