AtCoder AGC038F Two Permutations (网络流、最小割)
題目鏈接
https://atcoder.jp/contests/agc038/tasks/agc038_f
題解
好題。
首先觀察到一個(gè)性質(zhì),對(duì)于排列\(P\), 其所形成的每個(gè)輪換中的點(diǎn)\(A_i\)是選\(i\)還是選\(P_i\)的狀態(tài)必須相同。\(Q_i\)同理。
然后轉(zhuǎn)化成最小化\(A_i=B_i\)的位置\(i\)數(shù)量。
考慮\(A_i=B_i\)的條件:
(1) \(P_i=Q_i=i\), 則此位置無用,\(A_i=B_i\)一定滿足。
(2) \(P_i=i, Q_i\ne i\), 則\(A_i=B_i\)等價(jià)于\(B_i=i\).
(3) \(P_i\ne i, Q_i=i\), 則\(A_i=B_i\)等價(jià)于\(A_i=i\).
(4) \(P_i=Q_i\ne i\), 則\(A_i=B_i\)等價(jià)于\(A_i\)和\(B_i\)選的狀態(tài)(即是\(i\)還是\(P_i\)或\(Q_i\))不同。
(5) \(P_i\ne Q_i\ne i\), 則\(A_i=B_i\)等價(jià)于\(A_i=i\)且\(B_i=i\).
那么我們可以從中觀察到一個(gè)集合劃分模型: 把所有點(diǎn)劃分為\(S,T\)兩個(gè)集合,設(shè)\(A_i=i\)表示\(A_i\)在\(S\)集,\(A_i=P_i\)表示\(A_i\)在\(T\)集,\(B_i=i\)表示\(i\)在\(T\)集,\(B_i=Q_i\)表示\(i\)在\(S\)集。那么上述條件就可以轉(zhuǎn)化為:
對(duì)每個(gè)位置\(i\):
(1) \(P_i=Q_i\), 則此位置無用,一定要花費(fèi)\(1\)的代價(jià)。
(2) \(P_i=i, Q_i\ne i\), 則如果\(B_i\)在\(T\)集需要花費(fèi)\(1\)的代價(jià)。
(3) \(P_i\ne i, Q_i=i\), 則如果\(A_i\)在\(S\)集需要花費(fèi)\(1\)的代價(jià)。
(4) \(P_i=Q_i\ne i\), 則如果\(A_i\)和\(B_i\)在不同的集合要花費(fèi)\(1\)的代價(jià)。
(5) \(P_i\ne Q_i\ne i\), 則如果\(A_i\)在\(S\)集且\(B_i\)在\(T\)集需要花費(fèi)\(1\)的代價(jià)。
于是,給每個(gè)輪換建立一個(gè)點(diǎn),再對(duì)每個(gè)\(i\)分類討論,將其所對(duì)應(yīng)的輪換連邊即可。
由于建出來的圖是二分圖,且邊權(quán)都是\(1\), 故時(shí)間復(fù)雜度\(O(n\sqrt n)\).
我寫完之后一直TLE, 最后發(fā)現(xiàn)居然是我寫了兩年的當(dāng)前弧優(yōu)化一直是個(gè)假的……掛了38發(fā)真的是很無語……
代碼
#include<cstdio> #include<cstdlib> #include<iostream> #include<cstring> using namespace std;const int INF = 1e7;namespace NetFlow {const int N = 2e5+2;const int M = 4e5;struct Edge{int v,w,nxt,rev;} e[(M<<1)+3];int fe[N+3];int te[N+3];int dep[N+3];int que[N+3];int n,en,s,t;void addedge(int u,int v,int w){ // printf("addedge %d %d %d\n",u,v,w);en++; e[en].v = v; e[en].w = w;e[en].nxt = fe[u]; fe[u] = en; e[en].rev = en+1;en++; e[en].v = u; e[en].w = 0;e[en].nxt = fe[v]; fe[v] = en; e[en].rev = en-1;}bool bfs(){for(int i=1; i<=n; i++) dep[i] = 0;int head = 1,tail = 1; que[1] = s; dep[s] = 1;while(head<=tail){int u = que[head]; head++;for(int i=fe[u]; i; i=e[i].nxt){int v = e[i].v;if(e[i].w>0 && dep[v]==0){dep[v] = dep[u]+1;if(v==t)return true;tail++; que[tail] = v;}}}return false;}int dfs(int u,int cur){if(u==t||cur==0) {return cur;}int rst = cur;for(int &i=te[u]; i; i=e[i].nxt){int v = e[i].v;if(e[i].w>0 && rst>0 && dep[v]==dep[u]+1){int flow = dfs(v,min(rst,e[i].w));if(flow>0){e[i].w -= flow; rst -= flow;e[e[i].rev].w += flow;if(rst==0) {return cur;}}}}if(rst==cur) {dep[u] = -2;}return cur-rst;}int dinic(int _n,int _s,int _t){n = _n,s = _s,t = _t;int ret = 0;while(bfs()){for(int i=1; i<=n; i++) te[i] = fe[i];memcpy(te,fe,sizeof(int)*(n+1));ret += dfs(s,INF);}return ret;} } using NetFlow::addedge; using NetFlow::dinic;const int N = 1e5; int a[N+3],b[N+3]; int ca[N+3],cb[N+3]; int n,cnta,cntb;int main() {scanf("%d",&n);for(int i=1; i<=n; i++) scanf("%d",&a[i]),a[i]++;for(int i=1; i<=n; i++) scanf("%d",&b[i]),b[i]++;for(int i=1; i<=n; i++){if(!ca[i]){cnta++;int x = i;do{ca[x] = cnta;x = a[x];} while(x!=i);}}for(int i=1; i<=n; i++){if(!cb[i]){cntb++;int x = i;do{cb[x] = cntb;x = b[x];} while(x!=i);}} // printf("ca: "); for(int i=1; i<=n; i++) printf("%d ",ca[i]); puts(""); // printf("cb: "); for(int i=1; i<=n; i++) printf("%d ",cb[i]); puts("");int ans = n;for(int i=1; i<=n; i++){if(a[i]==i && b[i]==i) {ans--;}else if(a[i]==i) {addedge(1,cb[i]+cnta+2,1);}else if(b[i]==i) {addedge(ca[i]+2,2,1);}else{addedge(ca[i]+2,cb[i]+cnta+2,1);if(a[i]==b[i]) {addedge(cb[i]+cnta+2,ca[i]+2,1);}}}ans -= dinic(2+cnta+cntb,1,2);printf("%d\n",ans);return 0; }總結(jié)
以上是生活随笔為你收集整理的AtCoder AGC038F Two Permutations (网络流、最小割)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: AtCoder AGC038D Uniq
- 下一篇: AtCoder AGC032E Modu