bzoj1004 [HNOI2008]Cards 置换群+背包
【bzoj1004】[HNOI2008]Cards
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种洗牌法中的一种代替,且对每种
洗牌法,都存在一种洗牌法使得能回到原状态。
100%数据满足 Max{Sr,Sb,Sg}<=20。
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。
题解
置换的循环在不变元素中一定是一个颜色,所以只需要dp一下这个是属于那种颜色的
就是换了一种求不动点的方式
然后可以求一个三维的01背包的方案数。而最后的
除法需要利用扩展欧几里得求乘法的逆元。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 inline int read() 6 { 7 int x=0;char ch=getchar(); 8 while(ch<'0'||ch>'9')ch=getchar(); 9 while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} 10 return x; 11 } 12 int s1,s2,s3,n,m,mod,ans; 13 int a[70][70],f[70][70][70],d[70]; 14 bool b[70]; 15 int dp(int x) 16 { 17 for(int i=1;i<=n;i++)b[i]=0; 18 int sum=0,p; 19 for(int i=1;i<=n;i++) 20 if(!b[i]) 21 { 22 d[++sum]=1;p=i; 23 b[p]=1; 24 while(!b[a[x][p]]) 25 { 26 d[sum]++; 27 b[a[x][p]]=1; 28 p=a[x][p]; 29 } 30 } 31 for(int i=s1;i>=0;i--) 32 for(int j=s2;j>=0;j--) 33 for(int k=s3;k>=0;k--) 34 f[i][j][k]=0; 35 f[0][0][0]=1; 36 for(int h=1;h<=sum;h++) 37 for(int i=s1;i>=0;i--) 38 for(int j=s2;j>=0;j--) 39 for(int k=s3;k>=0;k--) 40 { 41 if(i>=d[h])f[i][j][k]=(f[i][j][k]+f[i-d[h]][j][k])%mod; 42 if(j>=d[h])f[i][j][k]=(f[i][j][k]+f[i][j-d[h]][k])%mod; 43 if(k>=d[h])f[i][j][k]=(f[i][j][k]+f[i][j][k-d[h]])%mod; 44 } 45 return f[s1][s2][s3]; 46 } 47 void exgcd(int a,int b,int &x,int &y) 48 { 49 if(b==0){x=1;y=0;return;} 50 exgcd(b,a%b,x,y); 51 int t=x;x=y;y=t-a/b*y; 52 } 53 int main() 54 { 55 s1=read(),s2=read(),s3=read(),m=read(),mod=read(); 56 n=s1+s2+s3; 57 for(int i=1;i<=m;i++) 58 for(int j=1;j<=n;j++) 59 a[i][j]=read(); 60 m++; 61 for(int i=1;i<=n;i++)a[m][i]=i; 62 for(int i=1;i<=m;i++) 63 ans=(ans+dp(i))%mod; 64 int x,y; 65 exgcd(m,mod,x,y); 66 while(x<=0)x+=mod,y-=m; 67 printf("%d",ans*x%mod); 68 return 0; 69 }