天天看点

bzoj1004: [HNOI2008]Cards

题目

#1.Burnside引理

Burnside引理与Polya定理

题解

//d[i]表示第i个循环的长度,b[i]是标记
#include<bits/stdc++.h>
using namespace std;
int f[22][22][22],i,b[62],a[62][62],n,m,ans,sr,sb,sg,M,x,d[62],j,y;
int dp(int x){
	memset(b,0,sizeof(b));
	int sum=0,p;
	for (int i=1;i<=n;i++)
		if (!b[i]){
			d[++sum]=0;
			for (p=i;!b[p];p=a[x][p]) d[sum]++,b[p]=1;
		}
	memset(f,0,sizeof(f));
	f[0][0][0]=1;
//刚开始把下面三个循环改成>=d[h],想省略三个判断,但事实上不能这么做
	for (int h=1;h<=sum;h++)
		for (int i=sr;i>=0;i--)
			for (int j=sb;j>=0;j--)
				for (int k=sg;k>=0;k--){
					if (i>=d[h]) f[i][j][k]=(f[i][j][k]+f[i-d[h]][j][k])%M;
					if (j>=d[h]) f[i][j][k]=(f[i][j][k]+f[i][j-d[h]][k])%M;
					if (k>=d[h]) f[i][j][k]=(f[i][j][k]+f[i][j][k-d[h]])%M;
				}
	return f[sr][sb][sg];
}
void ex_gcd(int a,int b,int &x,int &y){
	if (!b){
		x=1;y=0;
		return;
	}
	ex_gcd(b,a%b,y,x);
	y-=a/b*x;
}
int main(){
	scanf("%d%d%d%d%d",&sr,&sb,&sg,&m,&M);
	n=sr+sb+sg;
	for (i=1;i<=m;i++)
		for (j=1;j<=n;j++) scanf("%d",&a[i][j]);
	m++;
	for (i=1;i<=n;i++) a[m][i]=i;
	for (i=1;i<=m;i++) ans=(ans+dp(i))%M;
	ex_gcd(m,M,x,y);
	x=(x+M)%M;
	printf("%d",ans*x%M);
}
           

#2.组合数学

题解

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int ans,n,sr,sb,sg,m,M,i;
ll t;
int pm(int x,int y){
	int z=1;
	for (;y;y>>=1,x=x*x%M)
		if (y&1) z=z*x%M;
	return z;
}
int main(){
	scanf("%d%d%d%d%d",&sr,&sb,&sg,&m,&M);
	n=sr+sb;
	for (i=t=1;i<=sr;i++) t=t*(n-i+1)/i;//刚开始习惯性写成*=,好傻逼
	ans=t%M;
	n+=sg;
	for (i=t=1;i<=sg;i++) t=t*(n-i+1)/i;
	ans=t%M*ans%M;
	printf("%d",ans*pm(m+1,M-2)%M);
}
           

注:本题因为模数是质数,所以费马小定理和exgcd都可以求逆元