bzoj1004 [HNOI2008]Cards

1004: [HNOI2008]Cards

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 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

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 }
View Code

 

 
posted @ 2015-06-16 23:19  yanzx6  阅读(111)  评论(0编辑  收藏  举报