题目大意:给出一个置换群,求有多少种本质不同的染色方案。
思路:Burnside引理:置换群的等价类数目=所有置换的不动点数目的平均值。
有了这个引理,我们只需要求出所有不动点的数目求一个平均值就可以的到等价类的数目了。
要使一种染色的方案在一种置换的意义下是不动点,需要让这个置换的每个循环节中的颜色都相同。先求出所有置换的循环,然后用一个背包就可以初解了。最后乘法逆元搞一下除法。
CODE:
#define _CRT_SECURE_NO_DEPRECATE
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 110
using namespace std;
int R,G,B,cnt;
int displace,p;
struct Displace{
int change[MAX];
int loop[MAX],loops;
bool v[MAX];
void Read() {
for(int i = 1; i <= cnt; ++i)
scanf("%d",&change[i]);
}
void DFS(int x,int deep) {
if(v[x]) throw deep;
v[x] = true;
DFS(change[x],deep + 1);
}
void GetLoop() {
for(int i = 1; i <= cnt; ++i)
if(!v[i]) {
try{
DFS(i,0);
}catch(int len) {
loop[++loops] = len;
}
}
}
}src[MAX];
int f[MAX][MAX][MAX];
inline int Power(int x,int y)
{
int re = 1;
while(y) {
if(y&1) re = (re * x) % p;
y >>= 1;
x = (x * x) % p;
}
return re;
}
int main()
{
cin >> R >> G >> B >> displace >> p;
cnt = R + G + B;
for(int i = 1; i <= displace; ++i)
src[i].Read();
++displace;
for(int i = 1; i <= cnt; ++i)
src[displace].change[i] = i;
for(int i = 1; i <= displace; ++i)
src[i].GetLoop();
int total = 0;
for(int i = 1; i <= displace; ++i) {
memset(f,0,sizeof(f));
f[0][0][0] = 1;
for(int j = 1; j <= src[i].loops; ++j)
for(int r = R; ~r; --r)
for(int b = B; ~b; --b)
for(int g = G; ~g; --g) {
if(r - src[i].loop[j] >= 0) f[r][g][b] += f[r - src[i].loop[j]][g][b];
if(g - src[i].loop[j] >= 0) f[r][g][b] += f[r][g - src[i].loop[j]][b];
if(b - src[i].loop[j] >= 0) f[r][g][b] += f[r][g][b - src[i].loop[j]];
f[r][g][b] %= p;
}
total += f[R][G][B];
}
cout << total * Power(displace,p - 2) % p << endl;
return 0;
}BZOJ 1004 HNOI 2008 Cards Burnside引理
原文:http://blog.csdn.net/jiangyuze831/article/details/43051041