1. 程式人生 > >BZOJ1004 HNOI2008Cards(Burnside引理+動態規劃)

BZOJ1004 HNOI2008Cards(Burnside引理+動態規劃)

  直接給了一個置換群(當然要自己手動加上不洗牌的情況)。考慮求不動點數量即可。對於一個置換,求出所有迴圈的長度,然後設f[i][x][y]為給前i個迴圈著色後,用了x張紅色卡片、y張綠色卡片的方案數,dp一發即可。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define
N 66 char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;} int gcd(int n,int m){return m==0?n:gcd(m,n%m);} int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int n,A,B,C,m,P,a[N],cycle[N],f[N][N][N],ans; bool flag[N]; int calc() { memset(f,0,sizeof(f));f[0][0][0]=1;int s=0; for (int j=1;j<=n;j++) { s+=cycle[j]; for (int x=0;x<=A;x++)
for (int y=0;y<=B;y++) { int z=s-x-y;if (z>C) continue; if (x>=cycle[j]) f[j][x][y]+=f[j-1][x-cycle[j]][y]; if (y>=cycle[j]) f[j][x][y]+=f[j-1][x][y-cycle[j]]; if (z>=cycle[j]) f[j][x][y]+=f[j-1][x][y]; f[j][x][y]%=P; } } return f[n][A][B]; } int main() { #ifndef ONLINE_JUDGE freopen("bzoj1004.in","r",stdin); freopen("bzoj1004.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif A=read(),B=read(),C=read(),m=read(),P=read(); for (int i=1;i<=m;i++) { for (int j=1;j<=n;j++) a[read()]=j; memset(flag,0,sizeof(flag));n=0; for (int j=1;j<=n;j++) if (!flag[j]) { int x=a[j];flag[j]=1;cycle[++n]=1; while (x!=j) flag[x]=1,cycle[n]++,x=a[x]; } ans+=calc(); } n=A+B+C,m++;for (int i=1;i<=n;i++) cycle[i]=1;ans+=calc(); for (int i=1;i<P;i++) if (i*m%P==1) {ans=ans*i%P;break;} cout<<ans; return 0; }