先来写一写什么是置换群和Burside、Polya。
关于置换群、Burnside和Polya
一、一些概念
1、群:对于一个集合G={a,b,c,…}和G上的二元运算*,满足①封闭性②结合律③单位元④逆元,则称:集合G在运算’*’之下是一个群
2、置换群:置换群的元素是置换,运算是置换的连接
3、 Zk Z k (K不动置换类):设G是1~n的置换群,若K是1~n中的某个元素,G中使K保持不变的全体,记为 ZK Z K ,简称 K不动置换类
4、 Ek E k (等价类) :设G是1~n的置换群,若K是1~n中的某个元素,K在G作用下的轨迹,记作 Ek E k 。即K在G的作用下所能变化成的所有元素的集合
5、循环 :每个置换都可以写成若干互不相交的循环的乘积。两个循环 (a1 a2 ... an) ( a 1 a 2 . . . a n ) 和 (b1 b2 ... bn) ( b 1 b 2 . . . b n ) 互不相交是指对任意 i,j i , j , ai≠bj a i ≠ b j 。这样的表示是唯一的。置换的循环节数是上述表示中循环的个数
二、一些公式
1、 |Ek|⋅|Zk|=|G| | E k | · | Z k | = | G | , K K 不动置换类的元素个数 × 等价类的元素个数 = 置换群的元素个数
2、 ∑ni=1|Zi|=∑|G|j=1D(aj) ∑ i = 1 n | Z i | = ∑ j = 1 | G | D ( a j ) , D(aj) D ( a j ) 表示在置换 aj a j 下不变的元素的个数
对左式进行研究:
设
N=1,2,...,n
N
=
1
,
2
,
.
.
.
,
n
中共有
L
L
个等价类, ,有:
这里的 L L 就是我们要求的互异的组合状态的个数,于是得出:
这个式子就叫做Burside引理
3、构造置换群 G′={g1,g2,g3,g4} G ′ = { g 1 , g 2 , g 3 , g 4 } ,令 c(gi) c ( g i ) 表示 gi g i 的循环节数
gi g i 的同一个循环节中涂以相同颜色所得的图像数 mc(gi) m c ( g i ) ,恰好对应 G 中置换 ai a i 作用下不变的图像数 D(ai) D ( a i )
由此得出结论:
设 G 是 p 个对象的一个置换群,用 m 种颜色染色,本质不同的染色方案数为
其中 G={g1,g2,...,gs} G = { g 1 , g 2 , . . . , g s } , c(gi) c ( g i ) 为 gi g i 的循环节数
这就是所谓的polya定理
了解了什么是Burnside和Polya之后,来看两道题
[BZOJ 1004]Card
题目链接:BZOJ 1004
题目大意:有n张牌,m种洗牌方式,每张牌可以被染成红蓝绿三种颜色,颜色数有限制,问有多少种不同的染色方案,两种染色方案相同当且仅当其中一种通过任意次洗牌之后能洗成另一种。(m<=60,每种颜色数<=20)
题解:由于颜色数有限制,只能用Burnside定理,求出满足颜色数限制的条件下,每种置换的不动元素个数(不动元素是指经过置换变换后和没变化之前一样的染色方案)。置换的循环在不变元素中一定是同种颜色的,可以转化成三维背包来求。
code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
inline int read()
{
char c=getchar(); int num=0,f=1;
while (c<'0'||c>'9') { if (c=='-') f=-1; c=getchar(); }
while (c<='9'&&c>='0') { num=num*10+c-'0'; c=getchar(); }
return num*f;
}
int s1,s2,s3,n,m,mod,a[65][65],f[65][65][65],d[65],ans;
bool vis[65];
int dp(int x)
{
for (int i=1;i<=n;i++) vis[i]=false;
int tot=0;
for (int i=1;i<=n;i++) if (!vis[i])
{
d[++tot]=1; int p=i; vis[p]=true;
while (!vis[a[x][p]])
{
d[tot]++; vis[a[x][p]]=true;
p=a[x][p];
}
}
for (int i=s1;i>=0;i--)
for (int j=s2;j>=0;j--)
for (int k=s3;k>=0;k--) f[i][j][k]=0;
f[0][0][0]=1;
for (int cur=1;cur<=tot;cur++)
for (int i=s1;i>=0;i--)
for (int j=s2;j>=0;j--)
for (int k=s3;k>=0;k--)
{
if (i>=d[cur]) f[i][j][k]=(f[i][j][k]+f[i-d[cur]][j][k])%mod;
if (j>=d[cur]) f[i][j][k]=(f[i][j][k]+f[i][j-d[cur]][k])%mod;
if (k>=d[cur]) f[i][j][k]=(f[i][j][k]+f[i][j][k-d[cur]])%mod;
}
return f[s1][s2][s3];
}
void exgcd(int a,int b,int &x,int &y)
{
if (!b) x=1,y=0;
else exgcd(b,a%b,y,x),y-=a/b*x;
}
inline int inv(int x)
{
int u,v; exgcd(x,mod,u,v);
return (u%mod+mod)%mod;
}
int main()
{
s1=read(); s2=read(); s3=read(); m=read(); mod=read();
n=s1+s2+s3;
for (int i=1;i<=m;i++)
for (int j=1;j<=n;j++) a[i][j]=read();
m++; for (int i=1;i<=n;i++) a[m][i]=i;
for (int i=1;i<=m;i++) ans=(ans+dp(i))%mod;
ans=ans*inv(m)%mod;
printf("%d",ans);
return 0;
}
[BZOJ 1815]有色图
题目链接:BZOJ 1815
题目大意:n个点的无向完全图,m种颜色,给每一条边染色,两种方案相同当且仅当点经过重标号后能够使两张图同对应边颜色完全相同,问两两不同的染色方案数。(n<=53)
题解:(待更新)
code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
inline int read()
{
char c=getchar(); int num=0,f=1;
while (c<'0'||c>'9') { if (c=='-') f=-1; c=getchar(); }
while (c<='9'&&c>='0') { num=num*10+c-'0'; c=getchar(); }
return num*f;
}
int n,m,mod,jc[65],l[65],gd[65][65],ans;
int gcd(int a,int b) { return (!b)?a:gcd(b,a%b); }
LL ksm(LL a,LL b)
{
LL ret=1;
for (;b;b>>=1,a=a*a%mod)
if (b&1) ret=ret*a%mod;
return ret;
}
void calc(int tot)
{
LL c=0,now=1,tim=0,s=0;
for (int i=1;i<=tot;i++)
{
c+=l[i]/2; now=1ll*now*l[i]%mod;
for (int j=i+1;j<=tot;j++)
c+=gd[l[i]][l[j]];
if (l[i]==l[i-1]) tim++;
else now=1ll*now*jc[tim]%mod,tim=1;
}
if (tim) now=1ll*now*jc[tim]%mod;
s=1ll*jc[n]*ksm(now,mod-2)%mod;
ans=(ans+1ll*s*ksm(m,c)%mod)%mod;
}
void dfs(int tot,int x,int res)
{
if (res==0) { calc(tot); return; }
if (x>res) return; tot++;
while (x<=res) l[tot]=x,dfs(tot,x,res-x),x++;
}
int main()
{
n=read(); m=read(); mod=read();
jc[0]=1; for (int i=1;i<=n;i++) jc[i]=1ll*jc[i-1]*i%mod;
for (int i=1;i<=n;i++)
for (int j=i;j<=n;j++) gd[i][j]=gd[j][i]=gcd(i,j);
dfs(0,1,n); ans=1ll*ans*ksm(jc[n],mod-2)%mod;
printf("%d",ans);
return 0;
}