您的位置:首页 > 其它

bzoj1004: [HNOI2008]Cards [Burnside&Ploya+求逆元]

2014-09-19 22:38 253 查看
今晚还有点时间刷完水题以后把1004试着弄了一下,看上去好高端的~~

---------------------------------------------------------------------------------------------------

Burnside引理:C(f)为不动点个数,则可得等价类数目为C(f)的平均值

Ploya定理:置换f可以被分解为m(f)个循环,假设涂k种颜色,C(f)=k^m(f);

综上所述,等价类的个数等于所有置换的f的k^m(f)的平均数

---------------------------------------------------------------------------------------------------

但是首先这道题对于最终的等价类有一定颜色限制,所以不能直接用Ploya定理,那么退而求其次。

同样的我们还是要把置换f拆成m(f)个循环,显然不动点满足每一个循环内点颜色相同。

也就是我们需要找到一种求C(f)的方法,使得最终的等价类满足颜色的限制,颜色只有三种,我们尝试用DP代替。

假设循环内点有v个,我们把每一个循环当做一个体积为v的物品放入状态背包,并且要求最终状态为{Sr,Sb,Sg}

那么我们不难得到动归方程:

    f[i][j][k]=Sigma{f[i-v][j][k]+f[i][j-v][k]+f[i][j][k-v]};

那么我们就得到了满足条件的不动点个数 f[Sr][Sb][Sg] 加入答案

最后一步是求逆,实现起来并没有什么难度,可以采用扩展欧几里得或费马小定理。

p为质数这个性质太棒了!这意味着直接可以得到逆元x=(m+1)^(p-2) mod p,因为a模b的逆元为a^(b-2) mod b

其实如果懂了也没什么难的,只是不知道下次遇到同类的题会不会做QAQ

至于代码的实现,五十行不到,思路清晰的话还是比较短的。

--------------------------------------------------------------------------------------------------

#include<iostream>
#include<cstdio>
#include<cstring>
#define For(i,l,r) for(int (i)=(l);(i)<=(r);(i)++)
#define ForD(i,r,l) for(int (i)=(r);(i)>=(l);(i)--)
using namespace std;
const int N=70;
int sr,sb,sg,m,p,n,ans;
int zh
,f

,fm
;
bool used
;
int Qpow(int c,int k){
int ret=1;
while(k){
if (k&1) ret=(ret*c)%p;
c=(c*c)%p;
k>>=1;
}return ret;
}
int main(){
freopen("1004.in","r",stdin);
scanf("%d%d%d%d%d",&sr,&sb,&sg,&m,&p);
n=sr+sb+sg; ans=0;
For(i,1,m+1){
memset(f,0,sizeof(f));
memset(used,0,sizeof(used));
memset(fm,0,sizeof(fm));
if(i!=m+1) For(j,1,n) scanf("%d",&zh[j]);else For(j,1,n) zh[j]=j;
For(i,1,n) if(!used[i]){
int tmp=i;
used[i]=1;
fm[++fm[0]]=1;
while(zh[tmp]!=i){
tmp=zh[tmp];
used[tmp]=1;
fm[fm[0]]++;
}
}f[0][0][0]=1;
For(d,1,fm[0]) ForD(j,sr,0) ForD(k,sb,0) ForD(l,sg,0){
if(j-fm[d]>=0) f[j][k][l]=(f[j][k][l]+f[j-fm[d]][k][l])%p;
if(k-fm[d]>=0) f[j][k][l]=(f[j][k][l]+f[j][k-fm[d]][l])%p;
if(l-fm[d]>=0) f[j][k][l]=(f[j][k][l]+f[j][k][l-fm[d]])%p;
}ans=(ans+f[sr][sb][sg])%p;
}ans=(ans*Qpow(m+1,p-2))%p;
printf("%d\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: