Bzoj1004 [HNOI2008]Cards
小春现在很清闲,面对书桌上的N张牌,他决定给每张染色,目前小春只有3种颜色:红色,蓝色,绿色.他询问Sun有
多少种染色方案,Sun很快就给出了答案.进一步,小春要求染出Sr张红色,Sb张蓝色,Sg张绝色.他又询问有多少种方
案,Sun想了一下,又给出了正确答案. 最后小春发明了M种不同的洗牌法,这里他又问Sun有多少种不同的染色方案.
两种染色方法相同当且仅当其中一种可以通过任意的洗牌法(即可以使用多种洗牌法,而每种方法可以使用多次)洗
成另一种.Sun发现这个问题有点难度,决定交给你,答案可能很大,只要求出答案除以P的余数(P为质数).
哇原来burnside引理这么好玩的!棒!赶紧来切道题目~
我们知道Answer=Σf(i,r,b,g)/(m+1) 其中f(i,r,g,b)表示在第i组置换的不动点个数
那我们需要用dp来解决一下这个问题:
f[i-c[x]][j][k]+f[i][j-c[x]][k]+f[i][j][k-c[x]]->f[i][j][k]
c[x]表示当前置换中第i个循环的长度,显然每个循环的颜色必须相同
复杂度O(RGBM(R+G+B))
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int R,B,G,n,m,P,s[110],w[110],f[21][21][21],ans=0;
inline int pow(int x,int k,int& s){
for(;k;x=x*x%P,k>>=1) k&1?s=s*x%P:0;
}
int cal(){
int t=0;
for(int i=1;i<=n;++i) if(s[i]){
++t;
for(int j=i,k;s[j];j=s[j],s[k]=0) k=j,++w[t];
}
memset(f,0,sizeof f); ***f=1;
for(;t;--t)
for(int i=R;~i;--i)
for(int j=G;~j;--j)
for(int k=B;~k;--k)
f[i][j][k]=((i>=w[t]?f[i-w[t]][j][k]:0)+(j>=w[t]?f[i][j-w[t]][k]:0)+(k>=w[t]?f[i][j][k-w[t]]:0))%P;
return f[R][G][B];
}
int main(){
scanf("%d%d%d%d%d",&R,&G,&B,&m,&P); n=G+B+R;
for(int j=1;j<=n;++j) s[j]=j; ans=cal();
for(int i=1;i<=m;++i){
for(int j=1;j<=n;++j) scanf("%d",s+i);
ans=(ans+cal())%P;
}
pow(m+1,P-2,ans);
printf("%d\n",ans);
}