[bzoj1004][HNOI2008]Cards

[bzoj1004][HNOI2008]Cards

标签: 置换 Burnside引理


题目链接

扯淡

题目中说了这样一句话

两种染色方法相同当且仅当其中一种可以通过任意的洗牌法(即可以使用多种洗牌法,而每种方法可以使用多次)

怎么样?是不是很棘手。然而

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

题意

n张牌,3种颜色,给你一个置换集合G(且保证对于任意\(f,g\in G,fg \in G\)),让你对每张牌染色,使得红色Sred种,蓝色Sblue种,绿色Sgreen种。求不同的染色方案总数。

题解

根据burnside引理:等价类的个数等于每一个置换的不动点的个数之和的平均数。
如果是一个不动点的话,那么这个置换每一个循环中的元素颜色都一样。
这样我们就能跑一个三维背包了。

Code

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<set>
#include<queue>
#include<map>
#include<stack>
#include<vector>
using namespace std;
#define ll long long
#define REP(i,a,b) for(int i=(a),_end_=(b);i<=_end_;i++)
#define DREP(i,a,b) for(int i=(a),_end_=(b);i>=_end_;i--)
#define EREP(i,a) for(int i=start[(a)];i;i=e[i].next)
inline int read()
{
    int sum=0,p=1;char ch=getchar();
    while(!(('0'<=ch && ch<='9') || ch=='-'))ch=getchar();
    if(ch=='-')p=-1,ch=getchar();
    while('0'<=ch && ch<='9')sum=sum*10+ch-48,ch=getchar();
    return sum*p;
}

const int maxn=100;

int mod;

int dp[maxn][maxn][maxn],sr,sb,sg,n,m,ans;

int fa[maxn*3],w[maxn*3],cnt;

int fin(int x)
{
    return x==fa[x]?x:fa[x]=fin(fa[x]);
}

int power(int a,int b)
{
    int ans=1;
    while(b)
    {
        if(b & 1)ans=ans*a%mod;
        b>>=1;
        a=a*a%mod;
    }
    return ans;
}

void init()
{
    sr=read();sb=read();sg=read();n=sr+sb+sg;
    m=read();mod=read();
    REP(i,1,m+1)
    {
        REP(j,1,n)fa[j]=j;
        if(i<m+1)
        {
            REP(j,1,n)
            {
                int v=read();
                int x=fin(j),y=fin(v);
                if(x!=y)
                {
                    fa[y]=x;
                }
            }
        }
        memset(w,0,sizeof(w));
        cnt=0;
        REP(col,1,n)
        {
            int flag=1;
            REP(j,1,n)
            {
                if(fin(j)==col)
                {
                    if(flag)flag=0,cnt++;
                    w[cnt]++;
                }
            }
        }
        memset(dp,0,sizeof(dp));
        dp[0][0][0]=1;
        REP(j,1,cnt)
        {
            DREP(x,sr,0)
            {
                DREP(y,sb,0)
                {
                    DREP(z,sg,0)
                    {
                        if(x>=w[j])dp[x][y][z]+=dp[x-w[j]][y][z];
                        if(y>=w[j])dp[x][y][z]+=dp[x][y-w[j]][z];
                        if(z>=w[j])dp[x][y][z]+=dp[x][y][z-w[j]];
                        dp[x][y][z]%=mod;
                    }
                }
            }
        }
        ans=(ans+dp[sr][sb][sg])%mod;
    }
    cout<<ans*power(m+1,mod-2)%mod<<endl;
}

int main()
{
    freopen("input.in","r",stdin);
    freopen("output.out","w",stdout);
    init();
    return 0;
}

转载于:https://www.cnblogs.com/gzy-cjoier/p/7472722.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值