bzoj 1004: [HNOI2008]Cards 置换群+dp

来源:互联网 发布:数组splice 删除 编辑:程序博客网 时间:2024/05/29 14:05

题意

有n张卡片,m个置换,要给每张卡片染上三种颜色,每种颜色的个数固定,求有多少种本质不同的染色方案。

分析

burnside=1|G|(c(a1)+c(a2)+...+c(ak))

G表示置换的集合,ai表示置换(i=1,2..,k),c(ai)表示置换的不动点数量。

对于这题,我们可以把每种染色方案看成是一个点,然后对于一个置换,若一种染色方案经过某个置换后不变,那么这个染色方案的成为该置换的不动点。

显然一个置换下的每一个循环内的点的颜色都要是一样的。

那么问题就转换成了求每种置换下每个循环染的颜色都相同的不同染色方案有多少种,可以用一个三维背包来做,加起来除以置换数量即可。

代码

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>#define N 105#define LL long longusing namespace std;int s1,s2,s3,n,m,MOD,f[N][N][N],s[N],vis[N],a[N][N];int ksm(int x,int y){    int ans=1;    while (y)    {        if (y&1) ans=(LL)ans*x%MOD;        x=(LL)x*x%MOD;y>>=1;    }    return ans;}int dp(int x){    for (int i=1;i<=n;i++) vis[i]=0;    int tot=0;    for (int i=1;i<=n;i++)        if (!vis[i])        {            int p=i;vis[i]=1;s[++tot]=1;            while (!vis[a[x][p]])            {                p=a[x][p];vis[p]=1;s[tot]++;            }        }    for (int i=0;i<=s1;i++)        for (int j=0;j<=s2;j++)            for (int k=0;k<=s3;k++)                f[i][j][k]=0;    f[0][0][0]=1;    for (int l=1;l<=tot;l++)        for (int i=s1;i>=0;i--)            for (int j=s2;j>=0;j--)                for (int k=s3;k>=0;k--)                {                    if (i>=s[l]) f[i][j][k]=(f[i][j][k]+f[i-s[l]][j][k])%MOD;                    if (j>=s[l]) f[i][j][k]=(f[i][j][k]+f[i][j-s[l]][k])%MOD;                    if (k>=s[l]) f[i][j][k]=(f[i][j][k]+f[i][j][k-s[l]])%MOD;                }    return f[s1][s2][s3];}int main(){    scanf("%d%d%d%d%d",&s1,&s2,&s3,&m,&MOD);    n=s1+s2+s3;    for (int i=1;i<=m;i++)        for (int j=1;j<=n;j++)            scanf("%d",&a[i][j]);    m++;    for (int i=1;i<=n;i++) a[m][i]=i;    int ans=0;    for (int i=1;i<=m;i++)        ans=(ans+dp(i))%MOD;    ans=(LL)ans*ksm(m,MOD-2)%MOD;    printf("%d",ans);    return 0;}
0 0
原创粉丝点击