Bzoj1004 [HNOI2008]Cards
Submit: 3449 Solved: 2067
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
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。
Source
数学问题 置换群 burnside引理 动规 背包DP
对于每一种洗牌方式(置换),显然同一个循环节内(即通过该种洗牌方式)只能填同一个颜色。枚举每种置换,统计出其中的循环节大小和尺寸,01背包DP出方案即可。
1 /*by SilverN*/ 2 #include<algorithm> 3 #include<iostream> 4 #include<cstring> 5 #include<cstdio> 6 #include<cmath> 7 #include<vector> 8 #define LL long long 9 using namespace std; 10 const int mxn=105;; 11 int read(){ 12 int x=0,f=1;char ch=getchar(); 13 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 14 while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();} 15 return x*f; 16 } 17 int sr,sb,sg; 18 int n,m,p; 19 LL f[mxn][mxn][mxn]; 20 int a[mxn][mxn]; 21 int sz[mxn],cnt=0; 22 bool vis[mxn]; 23 LL dp(int x){ 24 // printf("dpx:%d\n",x); 25 int i,j,k,l; 26 memset(vis,0,sizeof vis); 27 cnt=0; 28 for(i=1;i<=n;i++){ 29 if(vis[i])continue; 30 vis[i]=1; 31 ++cnt; 32 sz[cnt]=1; 33 int t=a[x][i]; 34 while(t!=i){ 35 sz[cnt]++; 36 vis[t]=1; 37 t=a[x][t]; 38 } 39 } 40 memset(f,0,sizeof f); 41 f[0][0][0]=1; 42 for(i=1;i<=cnt;i++){ 43 // printf("sz:%d\n",sz[i]); 44 for(j=sr;j>=0;j--) 45 for(k=sb;k>=0;k--) 46 for(l=sg;l>=0;l--){ 47 if(j>=sz[i])f[j][k][l]=(f[j][k][l]+f[j-sz[i]][k][l])%p; 48 if(k>=sz[i])f[j][k][l]=(f[j][k][l]+f[j][k-sz[i]][l])%p; 49 if(l>=sz[i])f[j][k][l]=(f[j][k][l]+f[j][k][l-sz[i]])%p; 50 } 51 } 52 // printf("res%d:%lld\n",x,f[sr][sb][sg]); 53 return f[sr][sb][sg]; 54 } 55 LL ans=0; 56 LL ksm(LL a,LL k){ 57 LL res=1; 58 while(k){ 59 if(k&1)res=res*a%p; 60 a=a*a%p; 61 k>>=1; 62 } 63 return res; 64 } 65 int main(){ 66 int i,j; 67 sr=read();sb=read();sg=read();m=read();p=read(); 68 n=sr+sb+sg; 69 for(i=1;i<=m;i++) 70 for(j=1;j<=n;j++) 71 a[i][j]=read(); 72 m++; 73 for(i=1;i<=n;i++)a[m][i]=i; 74 LL inv=ksm(m,p-2); 75 for(i=1;i<=m;i++) 76 ans=ans+dp(i); 77 // printf("ans:%lld inv:%lld\n",ans,inv); 78 ans=ans*inv%p; 79 printf("%lld\n",ans); 80 return 0; 81 } 82
本文为博主原创文章,转载请注明出处。