BZOJ 1488 Luogu P4727 [HNOI2009]图的同构 (Burnside引理、组合计数)
題目鏈接
(Luogu) https://www.luogu.org/problem/P4727
(BZOJ) https://www.lydsy.com/JudgeOnline/problem.php?id=1488
題解
Burnside引理經典題。
首先考慮一個\(O(n!\times poly(n))\)暴力: 枚舉點的置換,然后計算在置換下保持不變的圖的個數。
把置換拆成若干個輪換。
(1) 考慮輪換內部: 假設一輪換為\((a_1\ a_2\ ...\ a_n)\), 那么\((a_1,a_2),(a_2,a_3),...,(a_n,a_1)\)這些邊要么都存在要么都不存在;\((a_1,a_3),(a_2,a_4),...,(a_{n-1},a_{1}),(a_{n},a_2)\)這些邊也要么都存在要么都不存在;一般地說,對于任何一個\(d\), 所有的\((a_i,a_{(i+d)\mod n})\)這些邊要么都存在要么都不存在,因此輪換內部一共有\(2^{\frac{n}{2}}\)種方案。
(2) 考慮輪換之間: 假設兩輪換分別為\((a_1,a_2,...,a_n),(b_1,b_2,...,b_m)\)則有: \((a_1,b_1),(a_2,b_2),...(a_i,b_i)\)這些邊存在情況都相同;\((a_1,b_2),(a_2,b_3),...,(a_i,b_{i+1})\)這些邊存在情況都相同;以此類推,可以得到兩輪換之間共有\(2^{\gcd(n,m)}\)種方案。
所有的置換方案數相加,最后除以置換總數\(n!\).
然后現在考慮\(n\le 60\)怎么辦。
當\(n\le 60\)時,我們可以枚舉拆分數(\(60\)的拆分數約為百萬級別)。
已知一個拆分的方案(方案是指一個無標號序列\(a\)滿足\(\sum a_i=n\),其長度為\(cnt\)),它對應了多少個不同排列的輪換分拆?
首先,長度為\(L\)的輪換共\((L-1)!\)種。
然后我們要處理標號問題。
假設輪換之間是有區別的,那么標號方案數為\(\frac{n!}{\prod^{cnt}_{i=1}a_i!}\).
但是長度相等的輪換之間沒有區別,所以除以\(\prod {num_i!}\), 其中\(num_i\)表示\(i\)在\(a\)中的出現次數。
最后乘起來得到\(\frac{n!}{\prod^{cnt}_{i=1}a_i\prod^n_{i=1}num_i!}\)
累加即可。
時間復雜度?
枚舉所有拆分方案,求\(cnt^2\)之和,我用程序計算得當\(n=60\)時該值約為\(2.7\times 10^8\).
代碼
#include<cstdio> #include<cstdlib> #include<cstring> #include<cassert> #include<iostream> #define llong long long using namespace std;inline int read() {int x=0; bool f=1; char c=getchar();for(;!isdigit(c);c=getchar()) if(c=='-') f=0;for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^'0');if(f) return x;return -x; }const int N = 60; const int P = 997; llong fact[N+3],finv[N+3],inv[N+3]; llong pw2[N+3]; int a[N+3]; int num[N+3]; int gcd[N+3][N+3]; int n,cnt; llong ans;int GCD(int x,int y) {return y==0?x:GCD(y,x%y); }llong quickpow(llong x,llong y) {llong cur = x,ret = 1ll;for(int i=0; y; i++){if(y&(1ll<<i)) {y-=(1ll<<i); ret = ret*cur%P;}cur = cur*cur%P;}return ret; }llong calc() {llong ret = fact[n];for(int i=1; i<=cnt; i++){ret = ret*inv[a[i]]%P;}for(int i=1; i<=n; i++){ret = ret*finv[num[i]]%P;}for(int i=1; i<=cnt; i++){ret = ret*pw2[a[i]>>1]%P;}for(int i=1; i<=cnt; i++){for(int j=i+1; j<=cnt; j++){ret = ret*pw2[gcd[a[i]][a[j]]]%P;}}return ret; }void dfs(int sum) {if(sum==n){ans = (ans+calc())%P;return;}for(int i=a[cnt]; i+sum<=n; i++){cnt++; a[cnt] = i; num[i]++;dfs(i+sum);a[cnt] = 0; cnt--; num[i]--;} }int main() {pw2[0] = 1ll; for(int i=1; i<=N; i++) pw2[i] = (pw2[i-1]<<1)%P;fact[0] = 1ll; for(int i=1; i<=N; i++) fact[i] = fact[i-1]*i%P;finv[N] = quickpow(fact[N],P-2); for(int i=N-1; i>=0; i--) finv[i] = finv[i+1]*(i+1)%P;for(int i=1; i<=N; i++) inv[i] = finv[i]*fact[i-1]%P;for(int i=1; i<=N; i++) for(int j=1; j<=N; j++) gcd[i][j] = GCD(i,j);scanf("%d",&n);if(n==0) {printf("1"); return 0;}a[0] = 1; dfs(0);ans = ans*finv[n]%P;printf("%lld\n",ans);return 0; }總結
以上是生活随笔為你收集整理的BZOJ 1488 Luogu P4727 [HNOI2009]图的同构 (Burnside引理、组合计数)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: BZOJ 2655 calc (组合计数
- 下一篇: BZOJ 1488 Luogu P472