【BZOJ1004】Cards(組合數學,Burnside引理)
【BZOJ1004】Cards(組合數學,Burnside引理)
題面
Description
小春現在很清閑,面對書桌上的N張牌,他決定給每張染色,目前小春只有3種顏色:紅色,藍色,綠色.他詢問Sun有
多少種染色方案,Sun很快就給出了答案.進一步,小春要求染出Sr張紅色,Sb張藍色,Sg張絕色.他又詢問有多少種方
案,Sun想了一下,又給出了正確答案. 最後小春發明了M種不同的洗牌法,這裏他又問Sun有多少種不同的染色方案.
兩種染色方法相同當且僅當其中一種可以通過任意的洗牌法(即可以使用多種洗牌法,而每種方法可以使用多次)洗
成另一種.Sun發現這個問題有點難度,決定交給你,答案可能很大,只要求出答案除以P的余數(P為質數).
Input
第一行輸入 5 個整數:Sr,Sb,Sg,m,p(m<=60,m+1<p<100)。n=Sr+Sb+Sg。
接下來 m 行,每行描述一種洗牌法,每行有 n 個用空格隔開的整數 X1X2...Xn,恰為 1 到 n 的一個排列,
表示使用這種洗牌法,第 i位變為原來的 Xi位的牌。輸入數據保證任意多次洗牌都可用這 m種洗牌法中的一種代
替,且對每種洗牌法,都存在一種洗牌法使得能回到原狀態。
Output
不同染法除以P的余數
Sample Input
1 1 1 2 7
2 3 1
3 1 2
Sample Output
2
HINT
有2 種本質上不同的染色法RGB 和RBG,使用洗牌法231 一次可得GBR 和BGR,使用洗牌法312 一次 可得BRG
和GRB。
100%數據滿足 Max{Sr,Sb,Sg}<=20。
題解
Burnside引理:
對於一個置換群
等價類的個數,等於所有置換的不動點的平均數
所以,這道題目相當於有\(m+1\)個置換
求總的不動點個數
其中,置換包括不變和題目給定的\(m\)個
然後是求不動點的個數的問題
每一個置換相當於若幹個循環
如果是不動點的話,
循環中的每個點的顏色應當相同
於是大力跑一個01背包就好了
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define MAX 100
#define MOD P
inline int read()
{
int x=0,t=1;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=-1,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*t;
}
int s1,s2,s3,m,P,n,ans;
int a[MAX][MAX],size[MAX];
int f[MAX][MAX][MAX];
bool vis[MAX];
int fpow(int a,int b)
{
int s=1;
while(b){if(b&1)s=1ll*s*a%P;a=1ll*a*a%P;b>>=1;}
return s;
}
int DP(int x)//求關於第x置換的不動點的個數
{
memset(size,0,sizeof(size));
memset(vis,0,sizeof(vis));
int sum=0;//拆成循環
for(int i=1;i<=n;++i)
if(!vis[i])
{
size[++sum]=1;vis[i]=true;
int p=i;
while(!vis[a[x][p]])size[sum]++,vis[p=a[x][p]]=true;
}
memset(f,0,sizeof(f));
f[0][0][0]=1;
for(int t=1;t<=sum;++t)
for(int i=s1;i>=0;--i)
for(int j=s2;j>=0;--j)
for(int k=s3;k>=0;--k)
{
if(i>=size[t])f[i][j][k]=(f[i][j][k]+f[i-size[t]][j][k])%MOD;
if(j>=size[t])f[i][j][k]=(f[i][j][k]+f[i][j-size[t]][k])%MOD;
if(k>=size[t])f[i][j][k]=(f[i][j][k]+f[i][j][k-size[t]])%MOD;
}
return f[s1][s2][s3];
}
int main()
{
s1=read();s2=read();s3=read();m=read();P=read();
n=s1+s2+s3;
for(int i=1;i<=m;++i)
for(int j=1;j<=n;++j)
a[i][j]=read();
++m;
for(int i=1;i<=n;++i)a[m][i]=i;//不動也是一種置換
for(int i=1;i<=m;++i)
ans=(ans+DP(i))%MOD;
printf("%lld\n",1ll*ans*fpow(m,P-2)%P);
return 0;
}
【BZOJ1004】Cards(組合數學,Burnside引理)