P1446 [HNOI2008]Cards [Burnside引理]

CardsCards


Description\mathcal{Description}
小春现在很清闲,面对书桌上的N张牌,他决定给每张染色,目前小春只有3种颜色:红色,蓝色,绿色.他询问Sun有多少种染色方案,Sun很快就给出了答案.

进一步,小春要求染出Sr张红色,Sb张蓝色,Sg张绿色.他又询问有多少种方案,Sun想了一下,又给出了正确答案. 最后小春发明了M种不同的洗牌法,这里他又问Sun有多少种不同的染色方案.两种染色方法相同当且仅当其中一种可以通过任意的洗牌法(即可以使用多种洗牌法,而每种方法可以使用多次)洗成另一种.

Sun发现这个问题有点难度,决定交给你,答案可能很大,只要求出答案除以P的余数(P为质数).

输入数据保证任意多次洗牌都可用这 m种洗牌法中的一种代替, 且对每种洗牌法, 都存在一种洗牌法使得能回到原状态 .


Solution\mathcal{Solution}

加粗字体保证了多次洗牌与使用一次其中一种洗牌方式的结果相同,
相当于给出的置换总数仍是 MM
.

对于MM置换, 根据 BurnsideBurnside引理,
我们只需将 MM 个置换下 状态始终不变的方案数 numinum_i 叠加,
再加上 题目没有给出的 单位置换 的方案数, 除 M+1M+1 即可得到答案.


numinum_i 可以使用 dpdp 求解,
在第 ii 个置换下, 含有sizesize个循环节, 每个循环节长度为 lenilen_i,

每个循环节内都必须染上同一个颜色才可以循环.
要求解的就是 三个不同的背包中 装 sizesize 个物品 .

F[i,j,k]F[i,j,k] 表示三种颜色分别使用 i,j,ki,j,k 个的方案数,
F[i,j,k]=F[ileni,j,k]+F[i,jleni,k]+F[i,j,kleni]F[i,j,k]=F[i-len_i,j,k]+F[i,j-len_i,k]+F[i,j,k-len_i],
最后 F[Sr,Sb,Sg]F[Sr,Sb,Sg] 即为该置换方案数.


Code\mathcal{Code}

#include<cstdio>
#include<cstring>
#define reg register

int Sr;
int Sb;
int Sg;
int M;
int N;
int P;
int mod;
int Ans;
int Len[75];
int len[75];
int Zh[75][75];
int F[75][75][75];

bool Used[75];

int gcd(int a, int b){ return !b?a:gcd(b, a%b); }

int Find_circle(int x){
        memset(Used, 0, sizeof Used);
        int s = 0;
        for(reg int i = 1; i <= N; i ++)
                if(!Used[i]){
                        Used[i] = 1;
                        len[++ s] = 1;
                        int to = Zh[x][i];
                        while(!Used[to]) Used[to] = 1, to = Zh[x][to], len[s] ++;
                }
        return s;
}

void Work(int size){
        memset(F, 0, sizeof F);
        F[0][0][0] = 1;
        for(reg int p = 1; p <= size; p ++)
                for(reg int i = Sr; ~i; i --)
                        for(reg int j = Sb; ~j; j --)
                                for(reg int k = Sg; ~k; k --){
                                        int &t = F[i][j][k];
                                        if(i >= len[p]) t = (1ll*t + F[i-len[p]][j][k]) % mod;
                                        if(j >= len[p]) t = (1ll*t + F[i][j-len[p]][k]) % mod;
                                        if(k >= len[p]) t = (1ll*t + F[i][j][k-len[p]]) % mod;
                                }
        Ans = (1ll*Ans + F[Sr][Sb][Sg]);
}

int KSM(int a, int b){
        a %= mod;
        int s = 1;
        while(b){
                if(b & 1) s = 1ll*s*a % mod;
                a = 1ll*a*a % mod;
                b >>= 1;
        }
        return s;
}

int main(){
        scanf("%d%d%d%d%d", &Sr, &Sb, &Sg, &M, &P);
        mod = P;
        N = Sr + Sb + Sg;
        for(reg int i = 1; i <= M; i ++)
                for(reg int j = 1; j <= N; j ++) scanf("%d", &Zh[i][j]);
        for(reg int i = 1; i <= M; i ++) Work(Find_circle(i));
        for(reg int i = 1; i <= N; i ++) len[i] = 1;
        Work(N);
        Ans = (1ll*Ans*KSM(M+1, mod-2)) % mod;
        printf("%d\n", Ans);
        return 0;
}


posted @ 2019-06-30 21:39  XXX_Zbr  阅读(129)  评论(0编辑  收藏  举报