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

    【题目链接】:http://www.lydsy.com/JudgeOnline/problem.php?id=1004

    【题意】

    给你sr+sb+sg张牌,(令n=sr+sb+sg),让你把这n张牌染成3种颜色(红蓝绿),且红色sr张,蓝色sb张,绿色sg张;
    同时再给你m个变化关系change[i],这里从左往右数第change[i]张牌可以移动到第i个位置;
    m行的变化关系每行都有n个change,即change[1..n]
    然后任意两种染色的方案只有在用m个变化关系不能互相到达时才认为不同;(每个变化可以任意次数使用)

    【题解】

    看了别人的题解,都说是burnside定理;
    这个定理的内容是说;
    m个变化,k种颜色染色;
    本质不同的染色方案数是
    m种变化中特殊的染色方案的和的平均数
    一种变化可能有多种特殊的染色方案;
    这里第i种变化的特殊方案指的是:
    在把每张纸染色之后,无论经过多少次第i行的这个变化,这n张纸的颜色还是维持一开始染色的那个样子;
    我们知道每一行的变化里面肯定有多个循环节,既然要前后不变;那么就让同一个循环节里面的纸的颜色一样就好;
    这样,我们可以一个变化一个变化的处理出m个变化的特殊的染色方案的和;
    然后再除m就好;
    这里对于求某一个变化的特殊染色方案数;
    可以用dp来搞;(背包方案数);
    假设这一行的变化有cnt个循环节;
    则对于每个循环节来说都有3种可能,染成红色或者染成蓝色或者染成绿色;
    这里背包的重量就是这个循环节的长度(即整个循环节都染成同一种颜色);
    写成3维的,然后逆序更新就好(按照01背包的更新方式);
    f[i][j][k]表示红色染了i个,蓝色染了j个,绿色染了k个的方案数;
    最后返回f[sr][sb][sg]就好;
    f[0][0][0]=1;
    因为答案涉及到了除法取模;
    所以还得写个乘法逆元;
    这里可以不用写扩展欧几里得了;
    又学了一个新的方法;
    在gcd(a,p)==1的时候(即互质),且p为质数;
    (a^(p-1))%p=1;
    则有(a*(a^(p-2)))%p=1;
    这里就能看出来了a^(p-2)就是a的乘法逆元;
    这里因为m+1<=p且p为质数;
    所以m不可能为p的倍数,则m和p肯定是互质的;
    也就满足上述条件了;
    写个快速幂呗;
    直接暴力乘也ok的吧.
    (感觉这个费马小定理版的乘法逆元好记多了..)

    【完整代码】

    /**************************************************************
        Problem: 1004
        User: chengchunyang
        Language: C++
        Result: Accepted
        Time:2132 ms
        Memory:11740 kb
    ****************************************************************/
    
    #include <bits/stdc++.h>
    using namespace std;
    #define lson l,m,rt<<1
    #define rson m+1,r,rt<<1|1
    #define LL long long
    #define rep1(i,a,b) for (int i = a;i <= b;i++)
    #define rep2(i,a,b) for (int i = a;i >= b;i--)
    #define mp make_pair
    #define pb push_back
    #define fi first
    #define se second
    #define rei(x) scanf("%d",&x)
    #define rel(x) scanf("%I64d",&x)
    
    typedef pair<int,int> pii;
    typedef pair<LL,LL> pll;
    
    const int dx[9] = {0,1,-1,0,0,-1,-1,1,1};
    const int dy[9] = {0,0,0,-1,1,-1,1,-1,1};
    const double pi = acos(-1.0);
    const int N = 110;
    
    int sr,sb,sg,m,p,bh[N][N],n;
    LL sumc = 0,f[N][N][N],siz[N],ny = 1;
    bool bo[N];
    
    LL js(int x)
    {
        int num = 0;
        memset(bo,false,sizeof bo);
        memset(siz,0,sizeof siz);
        memset(f,0,sizeof f);
        rep1(i,1,n)
        {
            if (bo[i]) continue;
            num++;
            int p = bh[x][i];
            while (!bo[p]) bo[p] = true,siz[num]++,p = bh[x][p];
        }
    
        f[0][0][0] = 1;
    
        rep1(i,1,num)
            rep2(j,sb,0)
                rep2(k,sr,0)
                    rep2(l,sg,0)
                        {
                            if (j>=siz[i]) f[j][k][l] = (f[j][k][l]+f[j-siz[i]][k][l])%p;
                            if (k>=siz[i]) f[j][k][l] = (f[j][k][l]+f[j][k-siz[i]][l])%p;
                            if (l>=siz[i]) f[j][k][l] = (f[j][k][l]+f[j][k][l-siz[i]])%p;
                        }
        return f[sb][sr][sg];
    }
    
    void ksm(int x)
    {
        if (!x) return;
        ksm(x>>1);
        ny = (ny*ny)%p;
        if (x&1)
            ny = (ny*m)%p;
    }
    
    int main()
    {
        //freopen("F:\rush.txt","r",stdin);
        rei(sr),rei(sb),rei(sg),rei(m),rei(p);
        n = sr+sb+sg;
        rep1(i,1,m)
            rep1(j,1,n)
                rei(bh[i][j]);
        m++;
        rep1(i,1,n)
            bh[m][i] = i;
        rep1(i,1,m)
            sumc = (sumc+js(i))%p;
        ksm(p-2);
        sumc = (sumc*ny)%p;
        cout << sumc << endl;
        return 0;
    }
  • 相关阅读:
    android 5.1 WIFI图标上的感叹号及其解决办法
    Recovery和Charger模式下屏幕旋转180度
    Android屏幕旋转总结
    Spring MVC 数据校验@Valid
    Spring注解装配
    Spring简单的REST例子
    Spring怎么引入多个xml配置文件
    spring使用c3p0报错
    Spring+JTA+Atomikos+MyBatis分布式事务管理
    (2-3)Eureka详解
  • 原文地址:https://www.cnblogs.com/AWCXV/p/7626627.html
Copyright © 2011-2022 走看看