zoukankan      html  css  js  c++  java
  • 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("***********
    ");
    #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;
    }
  • 相关阅读:
    二维码在短信业务应用的初步构思
    Twproject Gantt开源甘特图功能扩展
    vscode 1.5安装体验
    OpenLiveWriter代码插件
    golang语言构造函数
    WebAPI接口返回ArrayList包含Dictionary对象正确解析
    golang枚举类型
    Gatekeeper Pattern 把关(守门人)模式
    .NET Core Windows环境安装与体验
    Federated Identity Pattern 联合身份模式
  • 原文地址:https://www.cnblogs.com/planche/p/8493655.html
Copyright © 2011-2022 走看看