【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;
}
posted @ 2017-10-04 18:45  AWCXV  阅读(129)  评论(0编辑  收藏  举报