1. 程式人生 > >【Polya定理】BZOJ1488

【Polya定理】BZOJ1488

分析

據說很板的Polya定理的題。。。

可能我學了假的Polya。。。

首先回顧一下Polya定理的內容: 對n個染色位置的本質不同的染色方案數(即不能通過置換得到另一染色方案):

mck|G| 這裡的k是指一種置換,ck指這種置換的迴圈數。

當然,這個基礎公式很多時候並不實用,因此還有一種計算方式:

apmp|G| 這個就更加粗暴了,p是某種置換的迴圈個數,ap指迴圈個數為p個的置換有多少個。 這題就需要這個公式。

現在這題最大的難點是:置換是在點上的,然而染色是在邊上的。

因此問題就是:如何處理點置換與邊置換的對應關係。

把邊置換分為兩部分來看:邊的兩端在同一個點迴圈內的,邊的兩端在不同的點迴圈內的。

第一種情況

邊迴圈大小為點迴圈大小的一半(向下取整)。 證明據說很直觀: 列舉這個迴圈內的點置換(1+i,2+i,3+i,1)i[0,sum1]sum 那麼當i>sum2時,就可以在[0,sum2]中找一個與它相同。

第二種情況

這兩個點迴圈之間的邊迴圈大小,為兩個點迴圈大小的GCD。 證明據說也很簡單。。可以畫圖來證明? 附一個大佬的部落格

。。她已經寫得非常清楚了

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define SF scanf
#define PF printf
#define MAXN 65
#define MOD 997
using namespace std;
int fsp(int x,int y){
    int res=1;
    while(y){
        if(y&1)
            res=res*x%MOD;
        x=x*x%MOD;  
        y>>=1
; } return res; } int gcd(int x,int y){ if(y==0) return x; return gcd(y,x%y); } int a[MAXN],b[MAXN],fac[MAXN],ans,n,m; void solve(int x,int sum,int cnt){ if(sum==0){ int res=0,add=1; for(int i=1;i<=cnt;i++){ res+=(a[i]/2*b[i]+b[i]*(b[i]-1)/2*a[i])%(MOD-1); add=add*fac[b[i]]%MOD*fsp(a[i],b[i])%MOD; for(int j=i+1;j<=cnt;j++) res+=b[i]*b[j]*gcd(a[i],a[j]); res%=(MOD-1); } add=fac[n]*fsp(add,MOD-2)%MOD; ans+=add*fsp(m,res)%MOD; ans%=MOD; return ; } if(x==0) return ; for(int i=1;i*x<=sum;i++){ a[cnt+1]=x; b[cnt+1]=i; solve(x-1,sum-i*x,cnt+1); } solve(x-1,sum,cnt); } int main(){ freopen("color.in","r",stdin); freopen("color.out","w",stdout); SF("%d%d",&n,&m); fac[0]=1; for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%MOD; solve(n,n,0); ans*=fsp(fac[n],MOD-2); ans%=MOD; PF("%d",ans); }