zoukankan      html  css  js  c++  java
  • BZOJ 1004: [HNOI2008]Cards

    1004: [HNOI2008]Cards

    题意:有n张卡片,染成s1张红色,s2张蓝色和s3张绿色;之后有m种置换关系,问本质不同的染色方案有多少种?

    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。

    思路:对于输入的置换,先使用循环分解算法分解出cnt个循环,并且得出每个循环里面的元素个数为num;

    若是没有mod操作,直接就是所有置换f的K^(m(f))之和的平均值

    K表示颜色数,m(f)表示置换f将序列循环分解后的循环节数(即不相交循环的个数);

    这是因为在置换的作用下,每个循环内的元素的颜色必定相同;平均数即总的置换数;

    一个隐含的置换就是不操作,f[i] = i;

    难点:题目对每种颜色染色的数量做了规定,由于每个循环内所有元素的颜色是相同的,那么这个循环就必须作为一个整体进行染色;所以在01背包中,每个循环的大小就相当于物品的重量;注意每种颜色都是可能将一个循环染色的即可;

    在求出mod下的sum之后,将sum/m变成mod意义下的加法;

    即m*x = sum(mod p)   ==> m*x + p*y = sum(在exgcd()中求出的是 = gcd(m,p) = 1);

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<string.h>
    #include<algorithm>
    #include<vector>
    #include<cmath>
    #include<stdlib.h>
    #include<time.h>
    #include<stack>
    #include<set>
    #include<map>
    #include<queue>
    using namespace std;
    #define rep0(i,l,r) for(int i = (l);i < (r);i++)
    #define rep1(i,l,r) for(int i = (l);i <= (r);i++)
    #define rep_0(i,r,l) for(int i = (r);i > (l);i--)
    #define rep_1(i,r,l) for(int i = (r);i >= (l);i--)
    #define MS0(a) memset(a,0,sizeof(a))
    #define MS1(a) memset(a,-1,sizeof(a))
    #define MSi(a) memset(a,0x3f,sizeof(a))
    #define inf 0x3f3f3f3f
    #define lson l, m, rt << 1
    #define rson m+1, r, rt << 1|1
    //typedef __int64 ll;
    template<typename T>
    void read1(T &m)
    {
        T x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        m = x*f;
    }
    template<typename T>
    void read2(T &a,T &b){read1(a);read1(b);}
    template<typename T>
    void read3(T &a,T &b,T &c){read1(a);read1(b);read1(c);}
    template<typename T>
    void out(T a)
    {
        if(a>9) out(a/10);
        putchar(a%10+'0');
    }
    int s1,s2,s3,p,m,n;
    int vis[70],f[70][70][70],num[70],B[70][70];
    int DP(int id)
    {
        MS0(vis);
        int cnt = 0;
        rep1(i,1,n)if(!vis[i]){//循环的分解
            num[++cnt] = 0;
            int tmp = i;
            do{
                vis[tmp] = cnt;
                num[cnt]++;
                tmp = B[id][tmp];
            }while(!vis[tmp]);
        }
        MS0(f);
        f[0][0][0] = 1;
        rep1(i,1,cnt){
            rep_1(j,s1,0)
                rep_1(k,s2,0)
                    rep_1(q,s3,0){
                        if(j >= num[i]) (f[j][k][q] += f[j-num[i]][k][q]) %= p;
                        if(k >= num[i]) (f[j][k][q] += f[j][k-num[i]][q]) %= p;
                        if(q >= num[i]) (f[j][k][q] += f[j][k][q-num[i]]) %= p;
                }
        }
        return f[s1][s2][s3];
    }
    void exgcd(int a,int b,int& x,int& y)
    {
        if(b == 0){
             x = 1,y = 0;//等式右边为gcd(a,p) = 1;此时a = gcd(a,p);
             return ;
        }
        exgcd(b,a%b,y,x);
        y -= a/b*x;
    }
    
    int main()
    {
        read3(s1,s2,s3);
        read2(m,p);
        n = s1 + s2 + s3;
        rep1(i,1,m) rep1(j,1,n) read1(B[i][j]);
        ++m;//设置没变化的置换;
        rep1(i,1,n) B[m][i] = i;
        int sum = 0;
        rep1(i,1,m) sum += DP(i);//记录所有置换f的K^m(f)的和
        int x,y;// ans = sum/m;是在没有mod的时候
        exgcd(m,p,x,y);
        while(x < 0) x += p,y -= m;
        printf("%d
    ",sum*x%p);
        return 0;
    }
    View Code
  • 相关阅读:
    hdu 4614 线段树 二分
    cf 1066d 思维 二分
    lca 最大生成树 逆向思维 2018 徐州赛区网络预赛j
    rmq学习
    hdu 5692 dfs序 线段树
    dfs序介绍
    poj 3321 dfs序 树状数组 前向星
    cf 1060d 思维贪心
    【PAT甲级】1126 Eulerian Path (25分)
    【PAT甲级】1125 Chain the Ropes (25分)
  • 原文地址:https://www.cnblogs.com/hxer/p/5222893.html
Copyright © 2011-2022 走看看