- 要求将(n)张牌染成红、蓝、绿三色,每种颜色牌数分别为(S_r,S_b,S_g)。
- 给定(m)种洗牌方式,保证加上原置换之后形成一个置换群,求有多少种本质不同的染色方案。
- (S_r,S_b,S_gle20,mle60)
(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
",1LL*ans*QP(m+1,X-2)%X),0;//统计所有置换下不变方案数,除以置换总数m+1
}