bzoj 1004 Cards

1004: [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种洗牌法中的一种代替,且对每种洗牌法,都存在一种洗牌法使得能回到原状态。

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。

题解

此题我一开始看,没有半点思路,还以为是图论。后来,我去搜题解,说是裸题。当时我就怒了,觉得自己连裸题都做不出来。怼了好久之后怂了,又去看题解,群论裸题

首先此题需要burnside定理,即在集合\(X\)上的群\(G\),对于\(g \in G\),令\(X^g\)为在\(g\)作用下的不动元素,有\(|X/G|={1 \over {|G|}} \sum_{g \in G}|X^g|\)

在此题中就可以得出\(Ans = \sum Cal(change_i) * |G|^{-1}\),其中\(Cal\)计算每种置换的方案数。

Ps:

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int N = 65, M = 105;
int n, m, p, sr, sb, sg;
int dp[N][N][N], cha[M][N], d[N];
bool vis[N];
#define Inc(a,b) ((a) = ((a) + (b))%p)
int Cal(const int &x) {
    memset(dp, 0, sizeof dp); 
    memset(vis, 0, sizeof vis);
    int Sum = 0, t;
    for (int i = 1; i <= n; ++i) 
    if (!vis[i]) {
        d[++Sum] = 1; vis[t = i] = true;
        while (!vis[cha[x][t]]) {
            ++d[Sum]; t = cha[x][t];
            vis[t] = true;
        }
    }
    dp[0][0][0] = 1;
    for (t = 1; t <= Sum; ++t)
      for (int i = sr; ~i; --i)
        for (int j = sb; ~j; --j)
          for (int k = sg; ~k; --k) {
              if (i >= d[t]) Inc(dp[i][j][k], dp[i-d[t]][j][k]);
              if (j >= d[t]) Inc(dp[i][j][k], dp[i][j-d[t]][k]);
              if (k >= d[t]) Inc(dp[i][j][k], dp[i][j][k-d[t]]);
          }
    //printf("%d\n", dp[sr][sb][sg]);
    return dp[sr][sb][sg];
}
 
long long Mod_Pow(long long b, int c) {
    long long ans = 1LL;
    while (c) {
        if (c&1) ans = ans * b % p;
        b = b * b % p;
        c >>= 1;
    }
    return ans;
}
 
int main() {
    scanf("%d%d%d%d%d", &sr, &sb, &sg, &m, &p);
    n = sr + sb + sg;
    for (int i = 1; i <= m; ++i)
      for (int j = 1; j <= n; ++j)
        scanf("%d", cha[i] + j);
    ++m;
    long long ans = 0;
    for (int i = 1; i <= n; ++i) cha[m][i] = i;
    for (int i = 1; i <= m; ++i) Inc(ans, Cal(i));
    //printf("%d\n", ans);
    ans = ans * Mod_Pow(m, p - 2) % p;
    printf("%lld\n", ans);
    return 0;
}
posted @ 2016-09-16 22:19  cycleke  阅读(155)  评论(0编辑  收藏  举报