【BZOJ1004】Cards(组合数学,Burnside引理)

【BZOJ1004】Cards(组合数学,Burnside引理)

题面

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。

题解

Burnside引理:
对于一个置换群
等价类的个数,等于所有置换的不动点的平均数

所以,这道题目相当于有(m+1)个置换
求总的不动点个数
其中,置换包括不变和题目给定的(m)

然后是求不动点的个数的问题
每一个置换相当于若干个循环
如果是不动点的话,
循环中的每个点的颜色应当相同
于是大力跑一个01背包就好了

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define MAX 100
#define MOD P
inline int read()
{
    int x=0,t=1;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
int s1,s2,s3,m,P,n,ans;
int a[MAX][MAX],size[MAX];
int f[MAX][MAX][MAX];
bool vis[MAX];
int fpow(int a,int b)
{
    int s=1;
    while(b){if(b&1)s=1ll*s*a%P;a=1ll*a*a%P;b>>=1;}
    return s;
}
int DP(int x)//求关于第x置换的不动点的个数
{
    memset(size,0,sizeof(size));
    memset(vis,0,sizeof(vis));
    int sum=0;//拆成循环
    for(int i=1;i<=n;++i)
        if(!vis[i])
        {
            size[++sum]=1;vis[i]=true;
            int p=i;
            while(!vis[a[x][p]])size[sum]++,vis[p=a[x][p]]=true;
        }
    memset(f,0,sizeof(f));
    f[0][0][0]=1;
    for(int t=1;t<=sum;++t)
        for(int i=s1;i>=0;--i)
            for(int j=s2;j>=0;--j)
                for(int k=s3;k>=0;--k)
                {
                    if(i>=size[t])f[i][j][k]=(f[i][j][k]+f[i-size[t]][j][k])%MOD;
                    if(j>=size[t])f[i][j][k]=(f[i][j][k]+f[i][j-size[t]][k])%MOD;
                    if(k>=size[t])f[i][j][k]=(f[i][j][k]+f[i][j][k-size[t]])%MOD;
                }
    return f[s1][s2][s3];
}
int main()
{
    s1=read();s2=read();s3=read();m=read();P=read();
    n=s1+s2+s3;
    for(int i=1;i<=m;++i)
        for(int j=1;j<=n;++j)
            a[i][j]=read();
    ++m;
    for(int i=1;i<=n;++i)a[m][i]=i;//不动也是一种置换
    for(int i=1;i<=m;++i)
        ans=(ans+DP(i))%MOD;
    printf("%lld
",1ll*ans*fpow(m,P-2)%P);
    return 0;
}

原文地址:https://www.cnblogs.com/cjyyb/p/8158795.html