【解析】Burnside引理+背包dp+乘法逆元


[Analysis]
这道题卡了好久,就是没想懂置换跟着色是不一样的。

依据burnside引理。在一个置换群作用下不等价类的个数为每一个置换作用下不动点个数的平均数。
在这道题中:
置换的对象 —— 每一个状态,标号为1—N(这里的N不是题目的N,而是状态的个数)。


不动点 —— 前后染色状态全然同样的状态的个数。
所以就是求经过变换后前后状态全然同样的个数。


[Sum]

Burnside引理几个注意的地方

[1]什么是Burnside引理?
[2]置换的对象是什么?
[3]不动点意味着什么?
[4]着色变换不是置换。


[Code]

<span style="font-size:18px;">/**************************************************************
    Problem: 1004
    User: y20070316
    Language: C++
    Result: Accepted
    Time:328 ms
    Memory:2164 kb
****************************************************************/
 
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
 
const int N=70;
 
int Sr,Sb,Sg,n,m,p; //Basic
int map[N][N],d[N],v[N],cnt;    //Substitution
int f[N][N][N]; //Damatic Programming
int res;    //Answer
 
inline int read(void)
{
    int s=0,f=1; char c=getchar();
    for (;c<'0'||c>'9';c=getchar());
    for (;'0'<=c&&c<='9';c=getchar()) s=s*10+c-'0';
    return s*f;
}
 
void init(void)
{
    Sr=read(),Sb=read(),Sg=read(),n=Sr+Sb+Sg;
    m=read(),p=read();
    for (int i=1;i<=m;i++)
        for (int j=1;j<=n;j++) map[i][j]=read();
    m++; for (int j=1;j<=n;j++) map[m][j]=j;
}
 
void work(void)
{
    for (int i=1;i<=m;i++)
    {
        cnt=0; memset(v,0,sizeof v);
        memset(d,0,sizeof d);
         
        for (int j=1;j<=n;j++)
            if (!v[j])
            {
                v[j]=d[++cnt]=1;
                for (int k=map[i][j];k^j;k=map[i][k]) 
                    v[k]=1,d[cnt]++;
            }
             
        memset(f,0,sizeof f); f[0][0][0]=1;
        for (int i=1;i<=cnt;i++)
            for (int j=Sr;j>=0;j--)
                for (int k=Sb;k>=0;k--)
                    for (int q=Sg;q>=0;q--)
                    {
                        f[j][k][q]=0;
                        if (j>=d[i]) f[j][k][q]=(f[j][k][q]+f[j-d[i]][k][q])%p;
                        if (k>=d[i]) f[j][k][q]=(f[j][k][q]+f[j][k-d[i]][q])%p;
                        if (q>=d[i]) f[j][k][q]=(f[j][k][q]+f[j][k][q-d[i]])%p;
                    }
        res=(res+f[Sr][Sb][Sg])%p;
    }
}
 
int mi(int i,int j)
{
    if (!j) return 1;
    int s=mi(i,j>>1);
    s=s*s%p;
    if (j&1) s=s*i%p;
    return s;
}
 
void print(void)
{
    int inv=mi(m,p-2);
    res=res*inv%p;
    printf("%d\n",(res+p)%p);
}
 
int main(void)
{   
    init();
    work();
    print();
 
    return 0;
}</span>


posted on 2017-05-14 10:23  lxjshuju  阅读(155)  评论(0编辑  收藏  举报