[BZOJ1004][HNOI2008]Cards(Burnside引理+dp)
2018-03-08 14:01
337 查看
传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1004
让我们先来分析一下题目,题目中有这样几句话:
1、两种染色方法相同当且仅当其中一种可以通过任意的洗牌法(即可以使用多种洗牌法,而每种方法可以使用多次)洗成另一种
2、输入数据保证任意多次洗牌都可用这 m种洗牌法中的一种代替,且对每种洗牌法,都存在一种洗牌法使得能回到原状态。
ok让我们来看一下Burnside引理的定义
设G={a1,a2,…ag}是目标集[1,n]上的置换群。每个置换都写成不相交循环的乘积。
是在置换ak的作用下不动点的个数,也就是长度为1的循环的个数。通过上述置换的变换操作后可以相等的元素属于同一个等价类。若G将[1,n]划分成l个等价类,则:
等价类个数为:
(来自百度百科)
我们发现,burnside引理中的
”循环节“(循环节指一个颜色经过k次染色之后又可以变回来,k就是循环节的长度)
”等价类“(等价类指本质上相等的着色方案即’等价‘)
刚好何我们一开始提到的那两句话一样,那么我们就可以用burnside引理来解决这道题啦!
让我们在通俗一点的解释burnside引理:
有G个置换,对于第i种置换,我们有循环节为1的循环c(i)个,那么不重复的着色方案数就是
因为这题数据非常小,所以我们可以用O(m2∗sr∗sg∗sb)O(m2∗sr∗sg∗sb)的方法来做,但这只是数据很小的请假,如果数据在大一些,我们就需要用到polya定理了。
(待更新)
code:
让我们先来分析一下题目,题目中有这样几句话:
1、两种染色方法相同当且仅当其中一种可以通过任意的洗牌法(即可以使用多种洗牌法,而每种方法可以使用多次)洗成另一种
2、输入数据保证任意多次洗牌都可用这 m种洗牌法中的一种代替,且对每种洗牌法,都存在一种洗牌法使得能回到原状态。
ok让我们来看一下Burnside引理的定义
设G={a1,a2,…ag}是目标集[1,n]上的置换群。每个置换都写成不相交循环的乘积。
是在置换ak的作用下不动点的个数,也就是长度为1的循环的个数。通过上述置换的变换操作后可以相等的元素属于同一个等价类。若G将[1,n]划分成l个等价类,则:
等价类个数为:
(来自百度百科)
我们发现,burnside引理中的
”循环节“(循环节指一个颜色经过k次染色之后又可以变回来,k就是循环节的长度)
”等价类“(等价类指本质上相等的着色方案即’等价‘)
刚好何我们一开始提到的那两句话一样,那么我们就可以用burnside引理来解决这道题啦!
让我们在通俗一点的解释burnside引理:
有G个置换,对于第i种置换,我们有循环节为1的循环c(i)个,那么不重复的着色方案数就是
∑c(i)G∑c(i)G
因为答案在mod意义下并且mod是质数,所以除以G要变成逆元∑c(i)∗inv(G)∑c(i)∗inv(G)
现在的问题就是找c(i),那么我们用dp来找。如果要使循环节为1,那么我们只需要把互相影响的点染成同一个颜色即可,记录一个循环节的大小size然后三维背包即可。因为这题数据非常小,所以我们可以用O(m2∗sr∗sg∗sb)O(m2∗sr∗sg∗sb)的方法来做,但这只是数据很小的请假,如果数据在大一些,我们就需要用到polya定理了。
(待更新)
code:
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; const int maxn=110; ll powmod(int a,int b,int p) { ll ans=1; while(b!=0) { if(b&1) ans=(ans*a)%p; a=(a*a)%p; b>>=1; } return ans; } ll inv(int a,int p){return powmod(a,p-2,p);}//费马小定理计算逆元 int sr,sb,sg; int n,m; ll mod; int ch[maxn][maxn],size[maxn];//ch置换群,size循环节大小 ll f[maxn][maxn][maxn];//背包染色,互相可能影响的点染成同种颜色 的方案数 bool vis[maxn];//循环节用判断 ll dp(int x)//当前是第x个置换 { int cnt=0; memset(vis,false,sizeof(vis)); memset(size,0,sizeof(size)); for(int i=1;i<=n;i++) { if(vis[i]) continue; cnt++; int p=ch[x][i]; while(!vis[p]){vis[p]=true;size[cnt]++;p=ch[x][p];} } memset(f,0,sizeof(f)); f[0][0][0]=1; for(int i=1;i<=cnt;i++) for(int j=sr;j>=0;j--) for(int k=sb;k>=0;k--) for(int l=sg;l>=0;l--) { if(j>=size[i]) f[j][k][l]=(f[j][k][l]+f[j-size[i]][k][l])%mod; if(k>=size[i]) f[j][k][l]=(f[j][k][l]+f[j][k-size[i]][l])%mod; if(l>=size[i]) f[j][k][l]=(f[j][k][l]+f[j][k][l-size[i]])%mod; } return f[sr][sb][sg]; } int main() { scanf("%d%d%d%d%d",&sr,&sb,&sg,&m,&mod); n=sr+sb+sg; for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) scanf("%d",&ch[i][j]); m++;//注意不动也是一个置换 for(int i=1;i<=n;i++) ch[m][i]=i; ll ans=0; for(int i=1;i<=m;i++) ans=(ans+dp(i))%mod; ans=(ans*inv(m,mod))%mod; printf("%lld\n",ans); return 0; }
相关文章推荐
- [BZOJ1004][HNOI2008]Cards(Burnside引理+DP)
- bzoj1004: [HNOI2008]Cards(burnside引理+DP)
- [bzoj 1004][HNOI 2008]Cards(Burnside引理+DP)
- 【bzoj1004】[HNOI2008]Cards burnside引理+dp
- Bzoj1004:[HNOI2008]Cards:置换群+dp
- 【bzoj 1004】 Cards 【HNOI2008】
- BZOJ1004 [HNOI2008]Cards
- bzoj1004 [HNOI2008]Cards(burnside引理+背包dp)
- BZOJ1004: [HNOI2008]Cards
- 【BZOJ1004】【HNOI2008】Cards 群论 置换 burnside引理 背包DP
- BZOJ 1004 [HNOI2008]Cards
- bzoj1004 [HNOI2008]Cards【Burnside/Polya】
- [BZOJ1004][HNOI2008][Burnside引理][DP]Cards
- 【BZOJ1004】Cards(HNOI2008)-Burnside引理+DP+逆元
- BZOJ 1004 【HNOI2008】 Cards
- 【BZOJ 1004】 [HNOI2008]Cards 【burnside引理】
- 【数论】【动态规划】[BZOJ1004][HNOI2008]Cards
- kyeremal-bzoj1004-[HNOI2008]-cards-dp+polya
- [BZOJ1004][HNOI2008]Cards(置换群+背包+乘法逆元)
- Burnside引理和Polya定理 & [bzoj 1004] [HNOI2008]Cards:Burnside引理,动态规划