1. 程式人生 > >[學習筆記]等價類計數&Polya定理 bzoj1004 [HNOI2008]Cards

[學習筆記]等價類計數&Polya定理 bzoj1004 [HNOI2008]Cards

論述證明等部分略去,說實在的我也不是很懂。
直接說結論。記f是一個置換,然後有一個置換群,群的概念自行搜尋,記C(f)表示在置換f作用下不變的有多少種,那麼染色的方案數等於所有C的和的平均數。這個結論沒有問題,問題是如何計算C。
如果對染色沒有限制,那麼C=(顏色數量)的(置換中環的個數)次方。
如果有限制,例如bzoj1004,可以用dp算,演算法顯然不贅述。
程式碼:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define per(i,t,s) for(int i=t;i>=s;i--) #define S 23 #define N S*3 #define inv(n) fast_pow(n,p-2,p) using namespace std; int dp[S][S][S],A,B,C,n,m,p,ans; int fast_pow(int n,int k,int p) { if(k==0) return 1;if(k==1) return n%p; int ans=fast_pow(n,k>>1,p);ans=ans*ans%p; if(k&1) ans=ans*n%p;return ans;
} int to[N],cnt,t[N],r;bool vis[N]; int main() { scanf("%d%d%d%d%d",&A,&B,&C,&m,&p),n=A+B+C,r=++m; while(m--) { if(m) for(int i=1;i<=n;i++) scanf("%d",&to[i]); else for(int i=1;i<=n;i++) to[i]=i; memset(vis,false,sizeof(vis)),cnt=0; for(int i=1,x;i<=n;i++)
if(!vis[i]) { x=i,t[++cnt]=1,vis[x]=true; while(!vis[to[x]]) vis[x=to[x]]=true,t[cnt]++; } dp[0][0][0]=1; // printf("cnt=%d : ",cnt);rep(i,1,cnt) cout<<t[i]<<" ";cout<<endl; rep(i,1,cnt) per(a,A,0) per(b,B,0) per(c,C,0) { dp[a][b][c]=0; if(a>=t[i]) dp[a][b][c]+=dp[a-t[i]][b][c]; if(b>=t[i]) dp[a][b][c]+=dp[a][b-t[i]][c]; if(c>=t[i]) dp[a][b][c]+=dp[a][b][c-t[i]]; dp[a][b][c]%=p; } ans+=dp[A][B][C]; } return !printf("%d\n",ans%p*inv(r)%p); }