bzoj 1004
Pros
给若干个置换, 和三种颜色, 问有多少种本质不同的染色方案?
Sol
Burnside:
首先由 \(burnside\) 引理
$$Ans = \frac{\sum_{:i = 1}^{:m};f_i}{m}$$
其中 \(f_i\) 表示第 \(i\) 个置换的不动点的个数.
不动点的个数 就是在该置换下只有一种同构体的方案数.
置换的循环分解:
然后,前面 lyp 学长在这里讲,
关于一个置换能够分解成若干个环,
\(example:\)
\[\begin{equation}
\left(
\begin{array}{ccccc}
1 & 2 & 3 & 4 & 5\\
3 & 2 & 5 & 1 & 4\\
\end{array}
\right)
\end{equation}
\]
包含环:\(1 \rightarrow 3 \rightarrow 5 \rightarrow 4 \rightarrow 1\) 和 \(2 \rightarrow 2\)
对于任意一个环, 作用在上面的一次置换能够让这个环上面的元素循环移位一格.
不动点的条件
那么要让这个环能够成为不动点的充要条件就是,每个环上的颜色都是相同的.
然后,我们就能够 DP 了. 预处理环是学的别人的,感觉挺涨见识的.
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 100;
int n,m,sr,sb,sg,p,ans;
int f[maxn][maxn][maxn];
int g[maxn],vis[maxn],cir[maxn];
inline int dfs(int x){
return vis[x] ? 0 : (vis[x] = 1, dfs(g[x]) + 1);
}
inline void init(){
for(int i = 0; i < maxn; ++i) vis[i] = cir[i] = 0;
memset(f,0,sizeof(f)); ans = 0;
}
inline void add_mod(int &a,int b){
a = (a + b) % p;
}
inline int exgcd(int a,int b,int p){
return p % a ?(exgcd(b % a, a, (-p % a + a) % a) * b + p) / a : p / a;
}
int main()
{
freopen("card.in","r",stdin);
freopen("card.out","w",stdout);
scanf("%d %d %d %d %d\n",&sr,&sb,&sg,&m,&p);
n = sr + sb + sg; m++;
for(int i = 1; i <= m; ++i){
for(int j = 1; j <= n; ++j)
i == m ? g[j] = j : scanf("%d",&g[j]);
init();
for(int j = 1; j <= n; ++j) if(!vis[j]) cir[++cir[0]] = dfs(j);
f[0][0][0] = 1;
for(int j = 1; j <= cir[0]; ++j)
for(int k = 0; k <= sr; ++k)
for(int l = 0; l <= sb; ++l){
int &F = f[j][k][l], t = n - k - l;
if(k >= cir[j]) add_mod(F, f[j-1][k - cir[j]][l]);
if(l >= cir[j]) add_mod(F, f[j-1][k][l - cir[j]]);
if(t >= cir[j]) add_mod(F, f[j-1][k][l]);
}
ans = (ans + f[cir[0]][sr][sb]) % p;
}
ans = ans * exgcd(m, p, 1) % p;
printf("%d\n",ans);
return 0;
}