【Burnside定理/置换】[HNOI2008][HYSBZ/BZOJ1004]Cards
写在前面
如果你不知道知道置换,或者想要一种更快的方法,请前往【组合数学】[HNOI2008][HYSBZ/BZOJ1004]Cards.
题目链接
分析
根据Burnside定理,用
计算
每一个置换由T个循环节组成,每个循环节的颜色显然应该一样。我们可以处理出循环节。
用f[j][u][x][y][z]表示第j个置换第u个循环节染了x个红色,y个蓝色,z个绿色纸牌
最后要除以G,但是在前面的计算过程中已经mod p这么办?求逆元呀。
看看这里
#include<cstdio>
#include<algorithm>
#include<cstring>
#define MAXN 60
#define MAXM 60
#define MAXC 20
using namespace std;
int sr,sb,sg,m,p,x[MAXM+10][MAXM+10],f[MAXN+2][MAXC+2][MAXC+2][MAXC+2],ans,n;
void Read(int &x){
char c;
while(c=getchar(),c!=EOF)
if(c>='0'&&c<='9'){
x=c-'0';
while(c=getchar(),c>='0'&&c<='9')
x=x*10+c-'0';
ungetc(c,stdin);
return;
}
}
void read(){
Read(sr),Read(sb),Read(sg),Read(m),Read(p);
n=sr+sb+sg;
int i,j;
for(i=1;i<=m;i++)
for(j=1;j<=n;j++)
Read(x[i][j]);
}
bool vis[MAXN+10];
int quick_pow(int a,int b){
int ret=1;
while(b){
if(b&1)
ret=ret*a%p;
a=a*a%p;
b>>=1;
}
return ret;
}
void solve(){
int i,cnt,a[MAXN+10],u,xx,y,z,j;
m++;
for(i=1;i<=n;i++)
x[m][i]=i;
for(i=1;i<=m;i++){
memset(a,0,sizeof a);
cnt=0;
memset(vis,0,sizeof vis);
for(j=1;j<=n;j++)
if(!vis[j]){
cnt++;
while(!vis[j]){
vis[j]=1,a[cnt]++;
j=x[i][j];
}
}
memset(f,0,sizeof f);
f[0][0][0][0]=1;
for(u=1;u<=cnt;u++){
for(xx=0;xx<=sr;xx++)
for(y=0;y<=sb;y++)
for(z=0;z<=sg;z++){
if(xx>=a[u])
f[u][xx][y][z]+=f[u-1][xx-a[u]][y][z];
if(y>=a[u])
f[u][xx][y][z]+=f[u-1][xx][y-a[u]][z];
if(z>=a[u])
f[u][xx][y][z]+=f[u-1][xx][y][z-a[u]];
f[u][xx][y][z]%=p;
}
ans=(ans+f[cnt][sr][sb][sg])%p;
}
}
ans=ans*quick_pow(m,p-2)%p;
}
int main()
{
read();
solve();
printf("%d\n",ans);
}