Luogu P4708 画画 (Burnside引理、组合计数、划分数)
題目鏈接
https://www.luogu.org/problem/P4708
題解
看上去Luogu P4706-4709是Sdchr神仙出的一場(chǎng)比賽,一道水題和三道很有趣的題終于全過(guò)了紀(jì)念QAQ(然而后三道都看了題解)
以及為啥這題AC代碼幾乎全是打表。。
前置題目: BZOJ1488 求\(n\)個(gè)點(diǎn)無(wú)標(biāo)號(hào)無(wú)向圖個(gè)數(shù)。(歡迎閱讀 https://www.cnblogs.com/suncongbo/p/11295453.html )
沒(méi)做過(guò)的建議先去做一下那題。
這道題依然是枚舉拆分?jǐn)?shù),然后現(xiàn)在的問(wèn)題就是給定一些長(zhǎng)度的輪換求有多少個(gè)點(diǎn)度均為偶數(shù)的圖滿足經(jīng)過(guò)這些輪換作用后依然不變。
首先一個(gè)性質(zhì)是,同一輪換內(nèi)的所有點(diǎn)度數(shù)相同。(顯然)
考慮輪換內(nèi)部的邊,假設(shè)這個(gè)輪換長(zhǎng)度為\(L=2s+1\), 那么則有\(s\)種不同的邊組,每一種邊組會(huì)使得輪換內(nèi)所有點(diǎn)度數(shù)\(+2\); 若輪換長(zhǎng)度為\(2s\), 則有\(s\)種不同的邊組,其中\((s-1)\)種(除了對(duì)角的之外)使得所有點(diǎn)度數(shù)\(+2\), \(1\)種使得所有點(diǎn)度數(shù)\(+1\). 度數(shù)\(+2\)不改變奇偶性的顯然可以不考慮,直接答案乘以\(2\).
再考慮輪換之間的邊,假設(shè)兩輪換長(zhǎng)度分別為\(a,b\)則有\(\gcd(a,b)\)種邊組,每種邊組內(nèi)包含\(\text{lcm}(a,b)\)條邊,會(huì)給\(a\)中的點(diǎn)度數(shù)\(+\frac{\text{lcm}(a,b)}{a}\),給\(b\)中的點(diǎn)度數(shù)\(+\frac{\text{lcm(a,b)}}{b}\). 若二者都為偶數(shù),答案乘以\(2^{\gcd(a,b)}\). 若二者中恰有一個(gè)為奇數(shù),則相當(dāng)于有\(\gcd(a,b)\)次機(jī)會(huì)給奇數(shù)的那個(gè)輪換改變奇偶性。若二者都是奇數(shù),則相當(dāng)于有\(\gcd(a,b)\)次機(jī)會(huì)給兩個(gè)輪換同時(shí)改變奇偶性。
于是問(wèn)題轉(zhuǎn)化為: 有一張新圖(新圖里的點(diǎn)代表一個(gè)輪換),初始點(diǎn)權(quán)都為\(0\), 對(duì)每個(gè)點(diǎn)有\(c_i\)次機(jī)會(huì)改變它的奇偶性,對(duì)每條邊有\(d_i\)次機(jī)會(huì)同時(shí)改變兩端點(diǎn)的奇偶性。求有多少種方案使得最終所有點(diǎn)的點(diǎn)權(quán)為\(0\).
這個(gè)結(jié)論是,對(duì)于每一種改變點(diǎn)權(quán)總次數(shù)為偶數(shù)次的方案,都存在\(2^{\sum d_i-cnt+1}\)種邊操作方案(\(cnt\)為點(diǎn)數(shù))與之對(duì)應(yīng)。
感性理解,改變點(diǎn)權(quán)如果是奇數(shù)次顯然不行,偶數(shù)次的話可以構(gòu)建一棵DFS樹(shù),非樹(shù)邊任意操作之后樹(shù)邊按照自下而上操作總能操作完。注意這里官方題解寫(xiě)的是錯(cuò)的!官方題解直接寫(xiě)成\(2^{\sum d_i}\)坑了我一晚上……
那么答案就是\(2^{\sum c_i-1}\times 2^{\sum d_i-cnt+1}\).
問(wèn)題解決。
時(shí)間復(fù)雜度同BZOJ 1488.
代碼
#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 = 50; const int P = 998244353; llong fact[N*N*N+3],finv[N*N*N+3],inv[N*N*N+3],pw2[N*N*N+3]; int gcd[N+3][N+3];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; }void initmath(int n) {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;pw2[0] = 1ll; for(int i=1; i<=n; i++) pw2[i] = pw2[i-1]*2ll%P; }int a[N+3],num[N+3]; int uf[N+3]; int c[N+3]; int n,cnt; llong ans;int findfa(int u) {int i = u;while(u!=uf[u]) {u = uf[u];}while(u!=uf[i]){int j = uf[i]; uf[i] = u; i = j;}return u; }void unionfa(int u,int v) {int x = findfa(u),y = findfa(v);if(x!=y) {uf[x] = y;} }llong calc() {for(int i=1; i<=cnt; i++) uf[i] = i,c[i] = 0;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;}llong retp = 0ll;for(int i=1; i<=cnt; i++){for(int j=i+1; j<=cnt; j++){int g = gcd[a[i]][a[j]],lcm = a[i]*a[j]/g;int ci = lcm/a[i],cj = lcm/a[j];if((ci&1)==(cj&1)){retp += g;if(ci&1) {unionfa(i,j);}}}}for(int i=1; i<=cnt; i++){for(int j=i+1; j<=cnt; j++){int g = gcd[a[i]][a[j]],lcm = a[i]*a[j]/g;int ci = lcm/a[i],cj = lcm/a[j];int x = findfa(i),y = findfa(j);if((ci&1)!=(cj&1)){if(ci&1) {c[x]+=g;}else if(cj&1) {c[y]+=g;}}}}for(int i=1; i<=cnt; i++){int x = findfa(i);retp += ((a[i]-1)>>1);if(!(a[i]&1)) {c[x]++;}}retp -= cnt;for(int i=1; i<=cnt; i++) {if(uf[i]==i) retp++;}for(int i=1; i<=cnt; i++){if(uf[i]==i && c[i]>0){retp += c[i]-1;}}ret = ret*pw2[retp]%P;return ret; }void dfs(int sum) {if(sum==0){ans = (ans+calc())%P;}for(int i=a[cnt]; i<=sum; i++){cnt++; a[cnt] = i; num[i]++;dfs(sum-i);num[i]--; a[cnt] = 0; cnt--;} }int main() {initmath(N*N*N);for(int i=1; i<=N; i++) for(int j=1; j<=N; j++) gcd[i][j] = GCD(i,j);scanf("%d",&n);a[0] = 1; dfs(n);ans = ans*finv[n]%P;printf("%lld\n",ans);return 0; }總結(jié)
以上是生活随笔為你收集整理的Luogu P4708 画画 (Burnside引理、组合计数、划分数)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: BZOJ 1488 Luogu P472
- 下一篇: Luogu P4708 画画 (Burn