• [HNOI2008]Cards


    Time Limit: 10 Sec  Memory Limit: 162 MB
    Submit: 3719  Solved: 2236

    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。

    思路

     根据群论里brunside引理,我们可以知道:

    每一种洗牌方式,都是一个闭环,我们需要选择所有的单一操作下,循环节为1的方案的方案数之和,然后在除以方案数;

    需要注意的是,不洗牌也是一种操作;

    对于单一操作下,循环节为1的方案的方案数,我们可以通过背包DP求得;

    因为ans需要取模,同时题目有除法操作,所以还需要求逆元;

    对于样例的解释:

    不操作:6,(2,3,1):0,(3,1,2):0;

    ans=∑c()/(m+1)=6/3=2;

    代码实现

     1 #include<cstdio>
     2 #include<cstring>
     3 const int maxn=62;
     4 int n,m,sr,sb,sg,mod,ans;
     5 int s[maxn];
     6 int v[maxn],size[maxn];
     7 int f[maxn][maxn][maxn];
     8 int get_c(){
     9     int cnt=0,p;
    10     memset(v,0,sizeof(v));
    11     memset(size,0,sizeof(size));
    12     for(int i=1;i<=n;i++)
    13     if(!v[i]){
    14         p=s[i];
    15         cnt++;
    16         while(!v[p]) v[p]=1,size[cnt]++,p=s[p];
    17     }
    18     memset(f,0,sizeof(f));
    19     f[0][0][0]=1;
    20     for(int i=1,a=size[i];i<=cnt;i++,a=size[i])
    21     for(int r=sr;r>=0;r--)
    22     for(int b=sb;b>=0;b--)
    23     for(int g=sg;g>=0;g--){
    24         if(r>=a) f[r][b][g]+=f[r-a][b][g],f[r][b][g]%=mod;
    25         if(b>=a) f[r][b][g]+=f[r][b-a][g],f[r][b][g]%=mod;
    26         if(g>=a) f[r][b][g]+=f[r][b][g-a],f[r][b][g]%=mod;
    27     }
    28     return f[sr][sb][sg];
    29 }
    30 int ksm(int x,int k){
    31     int ret=1;
    32     while(k){
    33         if(k&1) ret*=x,ret%=mod;
    34         x*=x,x%=mod;
    35         k>>=1;
    36     }
    37     return ret;
    38 }
    39 int main(){
    40     scanf("%d%d%d%d%d",&sr,&sb,&sg,&m,&mod);
    41     n=sr+sb+sg;
    42     for(int i=1;i<=n;i++) s[i]=i;
    43     ans+=get_c(),ans%=mod;
    44     for(int i=1;i<=m;i++){
    45         for(int j=1;j<=n;j++) scanf("%d",&s[j]);
    46         ans+=get_c(),ans%=mod;
    47     }
    48     ans=(ans*ksm(m+1,mod-2))%mod;
    49     printf("%d
    ",ans);
    50     return 0;
    51 }
  • 相关阅读:
    编程ING:人人都能学会程序设计
    以拯救之因
    使用交互环境
    使用交互环境 连载3
    深入理解Oracle Exadata
    字符编码
    多语境的操作
    挖掘用户需求
    神一样的产品经理——基于移动与互联网产品实践
    产品各类型之间的关系
  • 原文地址:https://www.cnblogs.com/J-william/p/7485792.html
Copyright © 2020-2023  润新知