1. 程式人生 > >BZOJ1815 SHOI2006有色圖(Polya定理)

BZOJ1815 SHOI2006有色圖(Polya定理)

  置換數量是階乘級別的,但容易發現本質不同的點的置換數量僅僅是n的整數拆分個數,OEIS(或者寫個dp或者暴力)一下會發現不是很大,當n=53時約在3e5左右。

  於是暴力列舉點的置換,並且發現根據點的置換我們得到的實際上是邊的置換,暴力數一下迴圈節就好了。3e5*50*50,luogu上過掉了。誒怎麼bzoj上開的時限總共只有4s啊?

  考慮數邊置換的迴圈節時不那麼暴力。顯然兩端點在同一迴圈內的邊和在不同迴圈內的邊是不可能處於同一邊的迴圈的,並且第一種情況只與該迴圈長度有關,第二種情況只與兩迴圈長度有關,先預處理一下就可以了,當然事實上也能直接推出來。不過這複雜度上並沒有優化。

 

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 60
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,c,P,a[N],b[N],fac[N],inv[N],id[N][N],nxt[N*N],f[N],g[N][N],ans,T; bool flag[N*N]; int ksm(int a,int k) { int s=1; for (;k;k>>=1,a=1ll*a*a%P) if (k&1) s=1ll*s*a%P; return s; } int C(int n,int m){return 1ll*fac[n]*inv[m]%P*inv[n-m]%P;} int calc(int m) { int s=1,tot=n; for (int i=1;i<=m;i++) { int t=i; while (t<m&&a[t+1]==a[i]) t++; s=1ll*s*inv[t-i+1]%P; for (int j=i;j<=t;j++) s=1ll*s*C(tot,a[j])%P*fac[a[j]-1]%P,tot-=a[j]; i=t; } tot=0; for (int i=1;i<=m;i++) tot=(tot+f[a[i]])%P; for (int i=1;i<=m;i++) for (int j=i+1;j<=m;j++) tot=(tot+g[a[i]][a[j]])%P; return 1ll*s*ksm(c,tot)%P; } void dfs(int k,int n,int last) { if (n==0) ans=(ans+calc(k-1))%P; if (n<last) return; for (int i=last;i<=n;i++) a[k]=i,dfs(k+1,n-i,i); } int cycle() { int tot=0;memset(flag,0,T+1); for (int i=1;i<=T;i++) if (!flag[i]) { int x=nxt[i];flag[i]=1;tot++; while (x!=i) flag[x]=1,x=nxt[x]; } return tot; } int main() { #ifndef ONLINE_JUDGE freopen("bzoj1815.in","r",stdin); freopen("bzoj1815.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif n=read(),c=read(),P=read(); fac[0]=1;for (int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%P; inv[0]=inv[1]=1;for (int i=2;i<=n;i++) inv[i]=P-1ll*(P/i)*inv[P%i]%P; for (int i=2;i<=n;i++) inv[i]=1ll*inv[i-1]*inv[i]%P; for (int i=1;i<=n;i++) { T=0; for (int x=1;x<=i;x++) for (int y=x+1;y<=i;y++) id[x][y]=id[y][x]=++T; for (int x=1;x<=i;x++) for (int y=x+1;y<=i;y++) nxt[id[x][y]]=id[x%i+1][y%i+1]; f[i]=cycle(); } for (int i=1;i<=n;i++) for (int j=i;j<=n;j++) { T=0; for (int x=1;x<=i;x++) for (int y=1;y<=j;y++) id[x][y]=++T; for (int x=1;x<=i;x++) for (int y=1;y<=j;y++) nxt[id[x][y]]=id[x%i+1][y%j+1]; g[i][j]=g[j][i]=cycle(); } dfs(1,n,1); cout<<1ll*ans*inv[n]%P; return 0; }