您的位置:首页 > 其它

【BZOJ1004】Cards(HNOI2008)-Burnside引理+DP+逆元

2017-11-25 17:43 381 查看
测试地址:Cards

做法:本题需要用到Burnside引理+DP+逆元。

注意到,按照题目中给的定义,给出的m个置换加上回到自身的置换合在一起是一个大小为m+1的置换群,那么我们可以DP求出对于每个置换的等价类数目,然后使用Burnside引理求出最后答案。至于怎么DP,只要先求出每个置换中各个循环内的元素的个数,然后就可以使用O(n4)的三维背包求出了(这里n的最大值视为20,因为每种牌最多只有20张)。最后要乘上一个1/(m+1),转化成乘上m+1对p的逆元即可。总的时间复杂度为O(mn4)。

以下是本人代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int a,b,c,m,p,n,x[110];
int f[65][25][25][25],g[65],ans=0;
bool vis[65];

int power(int a,int b)
{
int s=1,ss=a;
while(b)
{
if (b&1) s=(s*ss)%p;
b>>=1;ss=(ss*ss)%p;
}
return s;
}

void work()
{
int tot=0;
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++)
if (!vis[i])
{
g[++tot]=0;
int now=i;
while(!vis[now]) vis[now]=1,g[tot]++,now=x[now];
}
memset(f,0,sizeof(f));
f[0][0][0][0]=1;
for(int i=1;i<=tot;i++)
{
for(int sa=0;sa<=a;sa++)
for(int sb=0;sb<=b;sb++)
for(int sc=0;sc<=c;sc++)
{
if (sa>=g[i]) f[i][sa][sb][sc]=(f[i][sa][sb][sc]+f[i-1][sa-g[i]][sb][sc])%p;
if (sb>=g[i]) f[i][sa][sb][sc]=(f[i][sa][sb][sc]+f[i-1][sa][sb-g[i]][sc])%p;
if (sc>=g[i]) f[i][sa][sb][sc]=(f[i][sa][sb][sc]+f[i-1][sa][sb][sc-g[i]])%p;
}
}
ans=(ans+f[tot][a][b][c])%p;
}

int main()
{
scanf("%d%d%d%d%d",&a,&b,&c,&m,&p);
n=a+b+c;
for(int i=1;i<=n;i++) x[i]=i;
work();
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++) scanf("%d",&x[j]);
work();
}
ans=(power(m+1,p-2)*ans)%p;
printf("%d",ans);

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: