【bzoj1004】[HNOI2008]Cards
1004: [HNOI2008]Cards
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 2928 Solved: 1754
[Submit][Status][Discuss]
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
2 3 1
3 1 2
Sample Output
HINT
有2 种本质上不同的染色法RGB 和RBG,使用洗牌法231 一次可得GBR 和BGR,使用洗牌法312 一次 可得BRG
和GRB。
100%数据满足 Max{Sr,Sb,Sg}<=20。
【题解】
染色法就相当于置换,要求的洗牌法就相当于等价类的个数。
那么根据burnside定理,ans就是每种置换下不动点的数目的和除以m
然而这道题关于颜色有限制,那么我们可以用f[i][j][k]表示用了i种颜色1,j种颜色2,k种颜色3的相同的方案数,b[h]表示循环节的长度,那么可以得到f[i][j][k]=f[i-d[h]][j][k]+f[i][j-d[h]][k]+f[i][j][k-d[h]]
求出ans之后,由于计算出的ans是取模后的结果,然后要除以m,然后。。。乘法逆元解决。
最后别忘了,除了题上给出的置换外,还有一个固定的置换,就是自身的置换。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<cmath> 6 #include<ctime> 7 #include<algorithm> 8 using namespace std; 9 int sr,sb,sg,m,n,mod,ans,a[61][61],b[61],f[61][61][61],d[61]; 10 inline int read() 11 { 12 int x=0;char ch=getchar(); 13 while(ch<'0'||ch>'9')ch=getchar(); 14 while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} 15 return x; 16 } 17 int dp(int x) 18 { 19 for(int i=1;i<=n;i++) b[i]=0; 20 int sum=0,p=0; 21 for(int i=1;i<=n;i++) 22 if(!b[i]) 23 { 24 p=i; b[p]=1; d[++sum]=1; 25 while(!b[a[x][p]]) 26 { 27 b[a[x][p]]=1; 28 d[sum]++; 29 p=a[x][p]; 30 } 31 } 32 for(int i=sr;i>=0;i--) 33 for(int j=sb;j>=0;j--) 34 for(int k=sg;k>=0;k--) 35 f[i][j][k]=0; 36 f[0][0][0]=1; 37 for(int h=1;h<=sum;h++) 38 for(int i=sr;i>=0;i--) 39 for(int j=sb;j>=0;j--) 40 for(int k=sg;k>=0;k--) 41 { 42 if(i>=d[h]) f[i][j][k]=(f[i][j][k]+f[i-d[h]][j][k])%mod; 43 if(j>=d[h]) f[i][j][k]=(f[i][j][k]+f[i][j-d[h]][k])%mod; 44 if(k>=d[h]) f[i][j][k]=(f[i][j][k]+f[i][j][k-d[h]])%mod; 45 } 46 return f[sr][sb][sg]; 47 } 48 void exgcd(int a,int b,int &x,int &y) 49 { 50 if(b==0) {x=1; y=0; return;} 51 exgcd(b,a%b,x,y); 52 int t=x; x=y; y=t-a/b*y; 53 } 54 int main() 55 { 56 sr=read(); sb=read(); sg=read(); m=read(); mod=read(); 57 n=sr+sb+sg; 58 for(int i=1;i<=m;i++) 59 for(int j=1;j<=n;j++) 60 a[i][j]=read(); 61 m++; 62 for(int i=1;i<=n;i++) a[m][i]=i; 63 for(int i=1;i<=m;i++) ans=(ans+dp(i))%mod; 64 int x,y; 65 exgcd(m,mod,x,y); 66 while(x<0) x+=mod; 67 printf("%d\n",ans*x%mod); 68 return 0; 69 }