bzoj1004 [HNOI2008]Cards
1004: [HNOI2008]Cards
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 2126 Solved: 1264
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。
大意:大抵是问在限制的颜色数量和m个变换方式下,不同的染色方案,其中不变也是一种变换方式
首先我们明确概念
把这m种变换方式叫做置换,
把所有的在颜色数量限制下(不考虑置换)的所有排列叫置换群,换种说法:所有不同的数列(即题目所求数列)加上他们通过m种置换置换出来的所有数列合起来叫置换群,
把无法通过这m种置换互相得到的数列的数目叫做等价计数(参考了别人的叫法)->即题目所求
把通过第 i 种置换无法置换(即置换后也是其本身)的数列叫做第 i 中置换的等价情况
两个定理(名字忘记了):
1、置换群中等价计数=所有置换下的等价情况的数量/置换种类的个数
2、置换的等价情况-》显然为所有循环节的内部的颜色相同的情况
根据定理可求出答案等价计数
在算法进行时需要不断取mod
除法转换为乘法,乘以逆元
逆元可用扩展欧几里德
证明:
假设b / a = m (% p)
b * x = m (% p)
所以 a * x = 1 (% p)
根据扩展欧几里德 ax+by=gcd(a,b)必定有解
所以(m+1)x+py = gcd(m+1, p) = 1
x即为(m+1)mod p下的逆元
对扩展欧几里德的推理:
ax+by=d=gcd(a, b)=gcd(b, a%b)
bx'+(a%b)y'=gcd(b,a%b)=d
令 a = kb+c
bx'+cy' = d
bx'+(a-kb)y'=d
ay'+b(x'-ky')=d
所以
x = y', y = x'-ky'
综上所述,本题得解
贴上代码
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cmath> 5 #include <deque> 6 #include <vector> 7 #include <queue> 8 #include <iostream> 9 #include <algorithm> 10 #include <map> 11 #include <set> 12 #include <ctime> 13 using namespace std; 14 typedef long long LL; 15 #define For(i, s, t) for(int i = (s); i <= (t); i++) 16 #define Ford(i, s, t) for(int i = (s); i >= (t); i--) 17 #define MIT (2147483647) 18 #define INF (1000000001) 19 #define MLL (1000000000000000001LL) 20 #define sz(x) ((bnt) (x).size()) 21 #define clr(x, y) memset(x, y, sizeof(x)) 22 #define puf push_front 23 #define pub push_back 24 #define pof pop_front 25 #define pob pop_back 26 #define ft first 27 #define sd second 28 #define mk make_pair 29 inline void SetIO(string Name) { 30 string Input = Name+".in", 31 Output = Name+".out"; 32 freopen(Input.c_str(), "r", stdin), 33 freopen(Output.c_str(), "w", stdout); 34 } 35 36 const int N = 70; 37 int A, B, C, n, m, p, Data[N]; 38 int Dp[2][N][N], Circle[N], Cir; 39 bool Visit[N]; 40 int Ans; 41 42 inline void Input() { 43 scanf("%d%d%d%d%d", &A, &B, &C, &m, &p); 44 n = A+B+C; 45 } 46 47 inline void Plus(int &A, int B) { 48 A += B; 49 if(A >= p) A -= p; 50 } 51 52 inline void Multiply(int &A, int B) { 53 A = (((LL) A)*((LL) B))%p; 54 } 55 56 inline int Work() { 57 clr(Visit, 0), Cir = 0; 58 For(i, 1, n) 59 if(!Visit[i]) { 60 Circle[++Cir] = 0; 61 for(int x = i; !Visit[x]; x = Data[x]) { 62 Visit[x] = 1; 63 Circle[Cir]++; 64 } 65 } 66 67 int Now = 0, Next = 1, t, Sum = 0; 68 clr(Dp, 0); 69 Dp[Now][0][0] = 1; 70 For(s, 1, Cir) { 71 Sum += Circle[s]; 72 clr(Dp[Next], 0); 73 For(i, 0, A) 74 For(j, 0, B) 75 if((t = Dp[Now][i][j]) > 0) { 76 if(i+Circle[s] <= A) 77 Plus(Dp[Next][i+Circle[s]][j], t); 78 if(j+Circle[s] <= B) 79 Plus(Dp[Next][i][j+Circle[s]], t); 80 if(Sum-i-j <= C) 81 Plus(Dp[Next][i][j], t); 82 } 83 Now ^= 1, Next ^= 1; 84 } 85 86 return Dp[Now][A][B]; 87 } 88 89 inline int Ext_Gcd(int a, int b, int &x, int &y) { 90 if(!b) { 91 x = 1, y = 0; 92 return a; 93 } else { 94 int t, tx, ty; 95 t = Ext_Gcd(b, a%b, tx, ty); 96 x = ty, y = tx-(a/b)*ty; 97 return t; 98 } 99 } 100 101 inline void Solve() { 102 Ans = 0; 103 int Ret; 104 For(i, 1, m) { 105 For(j, 1, n) scanf("%d", &Data[j]); 106 Ret = Work(); 107 Plus(Ans, Ret); 108 } 109 For(i, 1, n) Data[i] = i; 110 Ret = Work(); 111 Plus(Ans, Ret); 112 113 int Inv, t; 114 Ext_Gcd(m+1, p, Inv, t); 115 Inv = ((Inv%p)+p)%p; 116 Multiply(Ans, Inv); 117 118 printf("%d\n", Ans); 119 } 120 121 int main() { 122 SetIO("1004"); 123 Input(); 124 Solve(); 125 return 0; 126 }