BZOJ 1004 [HNOI2008]Cards

题解:burnside引理

DP出不动点的数目,用burnside引理算等价类

问题:不明白burnside引理的原理

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

int n,m,mm;
int Sr,Sb,Sg;
int tot;

int a[100];
int vis[100];
int b[100],nn;
int f[30][30][30];
void Calc(){
	nn=0;
	memset(vis,0,sizeof(vis));
	memset(f,0,sizeof(f));
	memset(b,0,sizeof(b));
	for(int i=1;i<=n;++i){
		if(vis[i])continue;
		++nn;
		int x=i;
		while(!vis[x]){
			vis[x]=1;b[nn]++;x=a[x];
		}
	}
//	for(int i=1;i<=nn;++i)cout<<b[i]<<' ';
//	cout<<endl;
	f[0][0][0]=1;
	for(int i=1;i<=nn;++i){
		for(int x=Sr;x>=0;--x){
			for(int y=Sb;y>=0;--y){
				for(int z=Sg;z>=0;--z){
					int t=b[i];
					if(x>=t)f[x][y][z]+=f[x-t][y][z];
					if(y>=t)f[x][y][z]+=f[x][y-t][z];
					if(z>=t)f[x][y][z]+=f[x][y][z-t];
					f[x][y][z]%=mm;
				}
			}
		}
	}
	tot=(tot+f[Sr][Sb][Sg])%mm;
}

int Ksm(int a,int p){
	int ret=1;
	for(;p;p>>=1,a=a*a%mm){
		if(p&1)ret=ret*a%mm;
	}
	return ret;
}

int main(){
	scanf("%d%d%d%d%d",&Sr,&Sb,&Sg,&m,&mm);
	n=Sr+Sb+Sg;
	for(int k=1;k<=m;++k){
		for(int i=1;i<=n;++i)scanf("%d",&a[i]);
		Calc();
	}
	m++;
	for(int i=1;i<=n;++i)a[i]=i;
	Calc();
	cout<<tot*Ksm(m,mm-2)%mm;
	return 0;
}

  

posted @ 2018-02-20 12:05  ws_zzy  阅读(144)  评论(0编辑  收藏  举报