【Burnside定理/置换】[HNOI2008][HYSBZ/BZOJ1004]Cards

写在前面

如果你不知道知道置换,或者想要一种更快的方法,请前往【组合数学】[HNOI2008][HYSBZ/BZOJ1004]Cards.

题目链接

分析

根据Burnside定理,用 D(aj) 表示在置换 aj 下不变的元素的个数。L表示本质不同的方案数,G表示置换群

L=1|G|i=1|G|D(aj)

计算 D(aj) 可以用DP;
每一个置换由T个循环节组成,每个循环节的颜色显然应该一样。我们可以处理出循环节。
用f[j][u][x][y][z]表示第j个置换第u个循环节染了x个红色,y个蓝色,z个绿色纸牌
fj,u,I,j,k=fj,u1,ia[u],j,k+fj,u1,i,ja[u],k+fj,u1,I,j,ka[u]

最后要除以G,但是在前面的计算过程中已经mod p这么办?求逆元呀。
看看这里

#include<cstdio>
#include<algorithm>
#include<cstring>
#define MAXN 60
#define MAXM 60
#define MAXC 20
using namespace std;
int sr,sb,sg,m,p,x[MAXM+10][MAXM+10],f[MAXN+2][MAXC+2][MAXC+2][MAXC+2],ans,n;
void Read(int &x){
    char c;
    while(c=getchar(),c!=EOF)
        if(c>='0'&&c<='9'){
            x=c-'0';
            while(c=getchar(),c>='0'&&c<='9')
                x=x*10+c-'0';
            ungetc(c,stdin);
            return;
        }
}
void read(){
    Read(sr),Read(sb),Read(sg),Read(m),Read(p);
    n=sr+sb+sg;
    int i,j;
    for(i=1;i<=m;i++)
        for(j=1;j<=n;j++)
            Read(x[i][j]);
}
bool vis[MAXN+10];
int quick_pow(int a,int b){
    int ret=1;
    while(b){
        if(b&1)
            ret=ret*a%p;
        a=a*a%p;
        b>>=1;
    }
    return ret;
}
void solve(){
    int i,cnt,a[MAXN+10],u,xx,y,z,j;
    m++;
    for(i=1;i<=n;i++)
        x[m][i]=i;
    for(i=1;i<=m;i++){
        memset(a,0,sizeof a);
        cnt=0;
        memset(vis,0,sizeof vis);
        for(j=1;j<=n;j++)
            if(!vis[j]){
                cnt++;
                while(!vis[j]){
                    vis[j]=1,a[cnt]++;
                    j=x[i][j];
                }
            }
        memset(f,0,sizeof f);
        f[0][0][0][0]=1;
        for(u=1;u<=cnt;u++){
            for(xx=0;xx<=sr;xx++)
                for(y=0;y<=sb;y++)
                    for(z=0;z<=sg;z++){
                        if(xx>=a[u])
                            f[u][xx][y][z]+=f[u-1][xx-a[u]][y][z];
                        if(y>=a[u])
                            f[u][xx][y][z]+=f[u-1][xx][y-a[u]][z];
                        if(z>=a[u])
                            f[u][xx][y][z]+=f[u-1][xx][y][z-a[u]];
                        f[u][xx][y][z]%=p;
                    }
            ans=(ans+f[cnt][sr][sb][sg])%p;
        }
    }
    ans=ans*quick_pow(m,p-2)%p;
}
int main()
{
    read();
    solve();
    printf("%d\n",ans);
}

你可能感兴趣的:(C++,数论,置换,bzoj,hnoi)