【洛谷1446】[HNOI2008] Cards(Burnside引理+简单背包)
- 要求将\(n\)张牌染成红、蓝、绿三色,每种颜色牌数分别为\(S_r,S_b,S_g\)。
- 给定\(m\)种洗牌方式,保证加上原置换之后形成一个置换群,求有多少种本质不同的染色方案。
- \(S_r,S_b,S_g\le20,m\le60\)
\(Burnside\)引理
给定颜色数目是无法\(Polya\)定理的,因此我们考虑\(Burnside\)引理:
\[L=\frac1{|G|}\sum_{j=1}^sD(a_j)
\]
对于一个置换\(p\),我们可以把它分成若干个环,那么想让一种染色方案在该置换下不变,就是要让每个环的颜色相同。
我们可以通过一个简单背包求解,即设\(f_{i,j,k}\)表示处理到第\(i\)个环,有\(j\)张红牌和\(k\)张蓝牌的方案数,那么\(D(p)\)就等于\(f_{n,S_r,S_b}\)。
代码:\(O(nmS^2)\)
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define S 20
#define Inc(x,y) ((x+=(y))>=X&&(x-=X))
using namespace std;
int n,m,s1,s2,s3,ans,X,p[3*S+5];
I int QP(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
int c[3*S+5],vis[3*S+5],f[3*S+5][S+5][S+5];I void DP()//简单背包
{
RI i,j,k,t=0;for(i=1;i<=n;++i) if(!vis[i]) {++t,j=i;W(!vis[j]) ++c[t],vis[j]=1,j=p[j];}//分成若干个环
for(i=0;i<=t;++i) for(j=0;j<=s1;++j) for(k=0;k<=s2;++k) f[i][j][k]=0;f[0][0][0]=1;//初始化DP数组
for(i=1;i<=t;++i) for(j=0;j<=s1;++j) for(k=0;k<=s2;++k) Inc(f[i][j][k],f[i-1][j][k]),//填绿色
j+c[i]<=s1&&Inc(f[i][j+c[i]][k],f[i-1][j][k]),k+c[i]<=s2&&Inc(f[i][j][k+c[i]],f[i-1][j][k]);//填红色;填蓝色
for(Inc(ans,f[t][s1][s2]),i=1;i<=n;++i) c[i]=vis[i]=0;//统计答案后清空
}
int main()
{
RI i,j;for(scanf("%d%d%d%d%d",&s1,&s2,&s3,&m,&X),n=s1+s2+s3,i=1;i<=n;++i) p[i]=i;DP();//原置换
for(i=1;i<=m;++i) {for(j=1;j<=n;++j) scanf("%d",p+j);DP();}return printf("%d\n",1LL*ans*QP(m+1,X-2)%X),0;//统计所有置换下不变方案数,除以置换总数m+1
}
待到再迷茫时回头望,所有脚印会发出光芒