• bzoj1004 [HNOI2008]Cards 置换群+背包


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

    100%数据满足 Max{Sr,Sb,Sg}<=20。

    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。

    题解

      置换的循环在不变元素中一定是一个颜色,所以只需要dp一下这个是属于那种颜色的

      就是换了一种求不动点的方式

      然后可以求一个三维的01背包的方案数。而最后的

      除法需要利用扩展欧几里得求乘法的逆元。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 using namespace std;
     5 inline int read()
     6 {
     7     int x=0;char ch=getchar();
     8     while(ch<'0'||ch>'9')ch=getchar();
     9     while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    10     return x;
    11 }
    12 int s1,s2,s3,n,m,mod,ans;
    13 int a[70][70],f[70][70][70],d[70];
    14 bool b[70];
    15 int dp(int x)
    16 {
    17     for(int i=1;i<=n;i++)b[i]=0;
    18     int sum=0,p;
    19     for(int i=1;i<=n;i++)
    20        if(!b[i])
    21        {
    22            d[++sum]=1;p=i;
    23            b[p]=1;
    24            while(!b[a[x][p]])
    25            {
    26                d[sum]++;
    27                b[a[x][p]]=1;
    28                p=a[x][p];
    29            }
    30        }
    31     for(int i=s1;i>=0;i--)
    32         for(int j=s2;j>=0;j--)
    33             for(int k=s3;k>=0;k--)
    34                 f[i][j][k]=0;
    35     f[0][0][0]=1;
    36     for(int h=1;h<=sum;h++)
    37         for(int i=s1;i>=0;i--)
    38             for(int j=s2;j>=0;j--)
    39                 for(int k=s3;k>=0;k--)
    40                 {
    41                     if(i>=d[h])f[i][j][k]=(f[i][j][k]+f[i-d[h]][j][k])%mod;
    42                     if(j>=d[h])f[i][j][k]=(f[i][j][k]+f[i][j-d[h]][k])%mod;
    43                     if(k>=d[h])f[i][j][k]=(f[i][j][k]+f[i][j][k-d[h]])%mod;
    44                 }
    45     return f[s1][s2][s3];
    46 }
    47 void exgcd(int a,int b,int &x,int &y)
    48 {
    49      if(b==0){x=1;y=0;return;}
    50      exgcd(b,a%b,x,y);
    51      int t=x;x=y;y=t-a/b*y;
    52 }
    53 int main()
    54 {
    55     s1=read(),s2=read(),s3=read(),m=read(),mod=read();
    56     n=s1+s2+s3;
    57     for(int i=1;i<=m;i++)
    58         for(int j=1;j<=n;j++)
    59             a[i][j]=read();
    60     m++;
    61     for(int i=1;i<=n;i++)a[m][i]=i;
    62     for(int i=1;i<=m;i++)
    63         ans=(ans+dp(i))%mod;
    64     int x,y;
    65     exgcd(m,mod,x,y);
    66     while(x<=0)x+=mod,y-=m;
    67     printf("%d",ans*x%mod);
    68     return 0;
    69 }
  • 相关阅读:
    SQL数据库inner join ,join,left join,full join(转)
    CSRF攻击(转)
    BZOJ1853: [Scoi2010]幸运数字
    BZOJ1935: [Shoi2007]Tree 园丁的烦恼
    BZOJ3289Mato的文件管理
    树状数组
    莫队算法
    如何在win上用Linux编c++
    Hash的应用
    关于指数循环节的证明
  • 原文地址:https://www.cnblogs.com/fengzhiyuan/p/7735008.html
Copyright © 2020-2023  润新知