P1446 [HNOI2008]Cards

题目描述

小春现在很清闲,面对书桌上的N张牌,他决定给每张染色,目前小春只有3种颜色:红色,蓝色,绿色.他询问Sun有多少种染色方案,Sun很快就给出了答案.

进一步,小春要求染出Sr张红色,Sb张蓝色,Sg张绿色.他又询问有多少种方案,Sun想了一下,又给出了正确答案. 最后小春发明了M种不同的洗牌法,这里他又问Sun有多少种不同的染色方案.两种染色方法相同当且仅当其中一种可以通过任意的洗牌法(即可以使用多种洗牌法,而每种方法可以使用多次)洗成另一种.

Sun发现这个问题有点难度,决定交给你,答案可能很大,只要求出答案除以P的余数(P为质数).

输入输出格式

输入格式:

第一行输入 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。

输出格式:

不同染法除以P的余数

输入输出样例

输入样例#1: 复制
1 1 1 2 7
2 3 1
3 1 2
输出样例#1: 复制
2

说明

有2 种本质上不同的染色法RGB 和RBG,使用洗牌法231 一次可得GBR 和BGR,使用洗牌法312 一次 可得BRG 和GRB。

 

分析:

先学习下几个引理

burnside引理

http://blog.csdn.net/liangzhaoyang1/article/details/72639208

逆元

https://www.cnblogs.com/linyujun/p/5194184.html

 参考了大神博客

http://tgotp.science/

 

思路:套用burnside引理,这题因为对颜色有限制 不能用 polya ,用动归求出对于一个置换的不动方案数 ;

dp的一些解释:dp[i][j][k]表示前i个循环, 用了1号颜色j个 2号颜色k个的方案数

num[n]表示该洗牌法下的循环节个数;

最后根据burnside引理:

不同的方案数:I=(C(a1)+C(a2)+...C(ag))/G

这题的情况是需要除以m

但是过程中我们都进行的%p

且(a  /  b) % p = (a%p  /  b%p) %p  是错误的,所以我们要用到逆元(这题因为p是质数,用费马小定理就行)

(a  /  b) % p = (a * inv(b) ) % p = (a % p * inv(b) % p) % p

费马小定理

a^(p-1) ≡1 (mod p)

所以 inv(a) = a^(p-2) (mod p)

 

// 去吧!皮卡丘! 把AC带回来!
//      へ     /|
//   /\7    ∠_/
//   / │   / /
//  │ Z _,< /   /`ヽ
//  │     ヽ   /  〉
//  Y     `  /  /
//  イ● 、 ●  ⊂⊃〈  /
//  ()  へ    | \〈
//   >ー 、_  ィ  │ //
//   / へ   / ノ<| \\
//   ヽ_ノ  (_/  │//
//    7       |/
//    >―r ̄ ̄`ー―_
//**************************************
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define inf 2147483647
const ll INF = 0x3f3f3f3f3f3f3f3fll;
#define ri register int
template <class T> inline T min(T a, T b, T c) { return min(min(a, b), c); }
template <class T> inline T max(T a, T b, T c) { return max(max(a, b), c); }
template <class T> inline T min(T a, T b, T c, T d) {
  return min(min(a, b), min(c, d));
}
template <class T> inline T max(T a, T b, T c, T d) {
  return max(max(a, b), max(c, d));
}
#define scanf1(x) scanf("%d", &x)
#define scanf2(x, y) scanf("%d%d", &x, &y)
#define scanf3(x, y, z) scanf("%d%d%d", &x, &y, &z)
#define scanf4(x, y, z, X) scanf("%d%d%d%d", &x, &y, &z, &X)
#define pi acos(-1)
#define me(x, y) memset(x, y, sizeof(x));
#define For(i, a, b) for (int i = a; i <= b; i++)
#define FFor(i, a, b) for (int i = a; i >= b; i--)
#define bug printf("***********\n");
#define mp make_pair
#define pb push_back
const int maxn = 3e5 + 10;
const int maxx = 1e6 + 10;
// name*******************************
int dp[80][30][30];
int a[80];
bool flag[80];
int num[80];
int s1, s2, s3, m, mod;
int tot;
int ans = 0;
int sum[80];
// function******************************
inline ll qmul(ll a, ll b) {
  ll base = a, ans = 1;
  while (b) {
    if (b & 1ll)
      ans = (ans * base) % mod;
    base = (base * base) % mod;
    b >>= 1ll;
  }
  return ans;
}
//***************************************
int main() {
  // ios::sync_with_stdio(0); cin.tie(0);
  // freopen("test.txt", "r", stdin);
  //  freopen("outout.txt","w",stdout);
  cin >> s1 >> s2 >> s3 >> m >> mod;
  m++;
  tot = s1 + s2 + s3;
  For(t, 1, m) {
    if (t == m)
      For(i, 1, tot) a[i] = i;
    else
      For(i, 1, tot) cin >> a[i];
    memset(flag, 0, sizeof(flag));
    memset(num, 0, sizeof(num));
    memset(sum, 0, sizeof(sum));
    int n = 0;
    For(j, 1, tot) {
      if (!flag[j]) {
        int cnt = 0;
        int p = j;
        while (!flag[p]) {
          flag[p] = 1;
          cnt++;
          p = a[p];
        }
        num[++n] = cnt;
        sum[n] = sum[n - 1] + cnt;
      }
    }
    dp[0][0][0] = 1;
    For(i, 1, n) {
      For(j, 0, s1) {
        For(k, 0, s2) {
          int l = sum[i] - j - k; //?
          if (l < 0 || l > s3)
            continue;
          int sum = 0;
          if (j >= num[i])
            sum = (sum + dp[i - 1][j - num[i]][k]) % mod;
          if (k >= num[i])
            sum = (sum + dp[i - 1][j][k - num[i]]) % mod;
          if (l >= num[i])
            sum = (sum + dp[i - 1][j][k]) % mod;
          dp[i][j][k] = sum;
          if (i == n)
            ans = (ans + dp[i][j][k]) % mod;
        }
      }
    }
  }
  int x = ans * qmul(m, mod - 2) % mod;
  cout << x;
  return 0;
}

 

posted @ 2018-03-02 15:54  planche  阅读(172)  评论(0编辑  收藏  举报