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; }