埋锅。。。BZOJ1004-置换群+burnside定理+
看这道题时当时觉得懵逼。。。这玩意完全看不懂啊。。。什么burnside。。。难受。。。
于是去看了点视频和资料,大概懂了置换群和burnside定理,亦步亦趋的懂了别人的代码,然后慢慢的打了出来。。。高兴的一匹。
回归正题啊,这个题如果大家不懂置换群的概念。。。是很难看的懂的,M种洗牌,代表了M种置换,加上自己本身一种,构成看M+1种置换,如果一种可以通过任意的洗牌法洗成另一种的看成一类(这就是等价类的定义),问有多少种染色方法???
这道题很明显嘛,就是算等价类的数目,这个需要用到burnside定理。。。
我们可以求所有循环长度为1的循环的个数,用burnside来求
对于置换i,求出它的循环节,每个循环节必须染成同一个颜色,但是对总数有限制
f[i][j][k]表示用i个红色,j个蓝色,k个绿色的方案数
把每个循环节视为一个物品,权值为长度。
f[i][j][k]=f[i-d[p]][j][k]+f[i][j-d[p]][k]+f[i][j][k-d[p]]
d[p]表示第p个循环节的长度
注意枚举顺序,p要在最外面,保证每个物品只取一次
置换的循环在不变元素中一定是一个颜色
嘤嘤。。。。我也没有懂DP啊。如果哪个大佬懂了,一定要给我说一下啊!!!
啊我懂了,这个dp[i]就代表这个循环的长度,那么我们f[i][j][k]=f[i-d[p]][j][k]+f[i][j-d[p]][k]+f[i][j][k-d[p]] 就代表这个循环分别全部是是红,蓝,绿的数目之和,求到最后的f[sr][sb][sg]就是全部的循环长度为1的个数。
代码
#include<iostream> #include<stdio.h> #include<string.h> #include<algorithm> #include<map> using namespace std; int sr,sb,sg,m,p; int a[100];//记录置换 int b[100];//b[i]表示循环长度为i int f[30][30][30];//f[i][j][k]在i个红色,j个黄色.k个蓝色 int vis[100];//记录是否是否在一个类里面 int n; int qpow(int a,int b){ int ans=1; while(b){ if (b&1)ans=ans*a%p; a=a*a%p; b/=2; } return ans%p; } int dfs(){ memset(f,0,sizeof(f)); memset(vis,0,sizeof(vis)); int tot=0; for (int i=1;i<=n;i++){//求循环的个数和阶数 if (!vis[i]){ vis[i]=1; tot++; b[tot]=1; int j=i; while(!vis[a[j]]){ j=a[j]; vis[j]=1; b[tot]++; } } } f[0][0][0]=1;//求所有循环长度为1的循环的个数 置换的循环在不变元素中一定是一个颜色 for (int z=1;z<=tot;z++){ for (int i=sr;i>=0;i--){ for (int j=sb;j>=0;j--){ for (int k=sg;k>=0;k--){ if (i>=b[z])f[i][j][k]+=f[i-b[z]][j][k]; if (j>=b[z])f[i][j][k]+=f[i][j-b[z]][k]; if (k>=b[z])f[i][j][k]+=f[i][j][k-b[z]]; f[i][j][k]=f[i][j][k]%p; } } } } return f[sr][sb][sg]; } int main(){ int ans; while(~scanf("%d%d%d%d%d",&sr,&sb,&sg,&m,&p)){ n=sr+sb+sg; ans=0; for(int i=1;i<=m;i++){ for (int j=1;j<=n;j++){ scanf("%d",&a[j]); } ans+=dfs(); ans=ans%p; } m++; for (int i=1;i<=n;i++)a[i]=i; ans+=dfs();ans=ans%p; ans=ans*qpow(m,p-2)%p; printf("%d\n",ans); } return 0; }
#include<iostream>#include<stdio.h>#include<string.h>#include<algorithm>#include<map>using namespace std;int sr,sb,sg,m,p;int a[100];//记录置换int b[100];//b[i]表示循环长度为iint f[30][30][30];//f[i][j][k]在i个红色,j个黄色.k个蓝色int vis[100];//记录是否是否在一个类里面int n;int qpow(int a,int b){ int ans=1; while(b){ if (b&1)ans=ans*a%p; a=a*a%p; b/=2; } return ans%p;}int dfs(){ memset(f,0,sizeof(f)); memset(vis,0,sizeof(vis)); int tot=0; for (int i=1;i<=n;i++){//求循环的个数和阶数 if (!vis[i]){ vis[i]=1; tot++; b[tot]=1; int j=i; while(!vis[a[j]]){ j=a[j]; vis[j]=1; b[tot]++; } } } f[0][0][0]=1;//求所有循环长度为1的循环的个数 置换的循环在不变元素中一定是一个颜色 for (int z=1;z<=tot;z++){ for (int i=sr;i>=0;i--){ for (int j=sb;j>=0;j--){ for (int k=sg;k>=0;k--){ if (i>=b[z])f[i][j][k]+=f[i-b[z]][j][k]; if (j>=b[z])f[i][j][k]+=f[i][j-b[z]][k]; if (k>=b[z])f[i][j][k]+=f[i][j][k-b[z]]; f[i][j][k]=f[i][j][k]%p; } } } } return f[sr][sb][sg];}int main(){ int ans; while(~scanf("%d%d%d%d%d",&sr,&sb,&sg,&m,&p)){ n=sr+sb+sg; ans=0; for(int i=1;i<=m;i++){ for (int j=1;j<=n;j++){ scanf("%d",&a[j]); } ans+=dfs(); ans=ans%p; } m++; for (int i=1;i<=n;i++)a[i]=i; ans+=dfs();ans=ans%p; ans=ans*qpow(m,p-2)%p; printf("%d\n",ans); } return 0;}