您的位置:首页 > 其它

bzoj1004: [HNOI2008]Cards(burnside引理+DP)

2017-06-07 23:57 459 查看

  题目大意:3种颜色,每种染si个,有m个置换,求所有本质不同的染色方案数。

  置换群的burnside引理,还有个Pólya过几天再看看。。。

  burnside引理:有m个置换k种颜色,所有本质不同的染色方案数就是每种置换的不变元素的个数的平均数。

  求每种置换的不变元素的个数用背包解决。因为置换之后元素不变,所以对于每个循环节我们要染一个颜色,于是先处理出循环节作为背包中的“物体”,然后一个三维背包解决。f[i][j][k]的i j k表示三种颜色分别还可以染多少次。

  除m%p用费马小定理就行了,我才不用exGCD...(QAQ因为老是忘记怎么写,快速幂多资磁

  没清零WA了2次。。。最近老是出小问题

  UPD:去看了一波polya定理,例题poj2409中,一开始我不理解为什么旋转1次和旋转2次要当做2个置换,看了群的概念才知道呜呜呜....  
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#define ll long long
using namespace std;
void read(ll &k)
{
k=0;int f=1;char c=getchar();
while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar();
while(c<='9'&&c>='0')k=k*10+c-'0',c=getchar();
k*=f;
}
ll sr,sb,sg,m,p,n,ans;
ll next[110],a[70][110],d[110],f[2][70][70][70];
ll dp(int x)
{
int cnt=0;
for(int i=1;i<=n;i++)next[i]=0;
for(int i=1;i<=n;i++)
if(!next[i])
{
d[++cnt]=next[i]=1;
int p=i;
while(!next[a[x][p]])
{
p=a[x][p];
next[p]=1;
d[cnt]++;
}
}
for(int i=0;i<=sr;i++)
for(int j=0;j<=sb;j++)
for(int k=0;k<=sg;k++)
f[1][i][j][k]=f[0][i][j][k]=0;
f[1][0][0][0]=1;
int now=0;
for(int l=1;l<=cnt;l++)
{
for(int i=0;i<=sr;i++)
for(int j=0;j<=sb;j++)
for(int k=0;k<=sg;k++)
{
if(i>=d[l])f[now][i][j][k]=(f[now^1][i-d[l]][j][k]+f[now][i][j][k])%p;
if(j>=d[l])f[now][i][j][k]=(f[now^1][i][j-d[l]][k]+f[now][i][j][k])%p;
if(k>=d[l])f[now][i][j][k]=(f[now^1][i][j][k-d[l]]+f[now][i][j][k])%p;
}
now^=1;
}
return f[now^1][sr][sb][sg];
}
ll mi(ll a,int b)
{
ll t=1,y=a;
while(b)
{
if(b&1)t=(t*y)%p;
y=(y*y)%p;
b>>=1;
}
return t%p;
}
int main()
{
read(sr);read(sb);read(sg);read(m);read(p);n=sr+sb+sg;
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
read(a[i][j]);
m++;
for(int i=1;i<=n;i++)a[m][i]=i;
for(int i=1;i<=m;i++)
ans=(ans+dp(i))%p;
printf("%lld\n",ans*mi(m,p-2)%p);
}
View Code
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: