bzoj 1004 burnside 引理+DP

  对于burnside引理需要枚举染色,这道题属于burnside的一种简单求解的方法,就是polya,我们可以使每一种置换中的循环节中的元素的颜色都相同,那么这样的话就可以直接DP了,我们可以将m个置换单独考虑,处理出当前置换中各个循环节,那么用w[aa][bb][cc]表示在使用了aa个颜色1,bb个颜色2,cc个颜色3时,我们的轨道数,那么我们可以通过背包来累加答案,w[aa][bb][cc]+=w[aa-b[i]][bb][cc] w[aa][bb][cc]+=w[aa][bb-b[i]][cc] w[aa][bb][cc]+=w[aa-b[i]][bb][cc-b[i]]。

  

/**************************************************************
    Problem: 1004
    User: BLADEVIL
    Language: C++
    Result: Accepted
    Time:84 ms
    Memory:868 kb
****************************************************************/
 
//By BLADEVIL
#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 70
 
using namespace std;
 
int sa,sb,sc,m,n,p,ans;
int a[maxn],b[maxn],w[25][25][25],flag[maxn];
 
int mi(int a,int k) { 
    int ans=1;
    while (k) {
        if (k&1) ans=(ans*a)%p;
        a=(a*a)%p;
        k>>=1;
    }
    return ans;
}
 
int main() {
    scanf("%d%d%d%d%d",&sa,&sb,&sc,&m,&p); n=sa+sb+sc;
    ans=1;
    for (int i=1;i<=n;i++) ans=(ans*i)%p;
    //printf("%d\n",ans);
    //printf("|%d\n",mi(3,3));
    for (int i=1;i<=sa;i++) ans=(ans*mi(i,p-2))%p;
    for (int i=1;i<=sb;i++) ans=(ans*mi(i,p-2))%p;
    for (int i=1;i<=sc;i++) ans=(ans*mi(i,p-2))%p;
    //printf("%d\n",ans);
    int cur=m;
    while (cur--) {
        for (int i=1;i<=n;i++) scanf("%d",&a[i]);
        memset(flag,0,sizeof flag);
        memset(b,0,sizeof b);
        memset(w,0,sizeof w);
        for (int i=1;i<=n;i++) if (!flag[i]) {
            b[++b[0]]=1; flag[i]=1;
            for (int cur=a[i];cur!=i;cur=a[cur]) b[b[0]]++,flag[cur]=1;
        }
        //for (int i=1;i<=b[0];i++) printf("%d ",b[i]); printf("\n");
        w[0][0][0]=1;
        for (int i=1;i<=b[0];i++)
            for (int aa=sa;aa;aa--)
                for (int bb=sb;bb;bb--)
                    for (int cc=sc;cc;cc--) {
                        if (aa>=b[i]) w[aa][bb][cc]=(w[aa][bb][cc]+w[aa-b[i]][bb][cc])%p;
                        if (bb>=b[i]) w[aa][bb][cc]=(w[aa][bb][cc]+w[aa][bb-b[i]][cc])%p;
                        if (cc>=b[i]) w[aa][bb][cc]=(w[aa][bb][cc]+w[aa][bb][cc-b[i]])%p;
                    }
        ans=(ans+w[sa][sb][sc])%p;
        //printf("%d\n",ans);   
    }
    ans=(ans*mi(m+1,p-2))%p;
    printf("%d\n",ans);
    return 0;
}

 

posted on 2014-03-25 07:44  BLADEVIL  阅读(385)  评论(0编辑  收藏  举报