初學最小割樹
阿新 • • 發佈:2021-06-11
前言
證明什麼的一概不會,問就是感性理解,總之先坑了。。。
最小割樹的構造
任取當前點集中的兩個點\(s,t\),求出\(s\)與\(t\)在原圖上的任意一組最小割(設大小為\(v\)),然後就可以在最小割樹上給\(s,t\)兩點間連一條邊權為\(v\)的邊。
接著,把點集按照最小割分成兩部分(即與\(s\)連通的一部分和與\(t\)連通的一部分),對兩部分的點分別遞迴構造。
顯然這樣一定能構造出一棵樹,這棵樹就叫最小割樹。
最小割樹的性質
任意兩點\(x,y\)間的最小割就是最小割樹上\(x,y\)間路徑中的最小邊權。
因此只要預處理一下就可以倍增查詢了。
程式碼:\(O(n^3m)\)
#include<bits/stdc++.h> #define Tp template<typename Ty> #define Ts template<typename Ty,typename... Ar> #define Reg register #define RI Reg int #define Con const #define CI Con int& #define I inline #define W while #define N 500 #define M 1500 #define LN 9 #define INF (int)1e9 using namespace std; int n,m; namespace FastIO { #define FS 100000 #define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++) #define pc(c) (FC==FE&&(clear(),0),*FC++=c) int OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS; I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;} Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));} Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);} Tp I void writeln(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc('\n');} }using namespace FastIO; namespace D//網路流 { #define add(x,y,f) (_[++ee].nxt=lnk[x],_[lnk[x]=ee].to=y,_[ee].F=f) int s,t,ee=1,lnk[N+5],cur[N+5];struct edge {int to,nxt,F;}e[2*M+5],_[2*M+5]; I void Add(CI x,CI y,CI f) {add(x,y,f),add(y,x,f);}//加一條無向邊 I void Init() {for(RI i=1;i<=ee;++i) e[i]=_[i];}//把原圖的邊複製一份 int q[N+5],d[N+5];I bool BFS() {RI i,k,H,T;for(i=1;i<=n;++i) d[i]=0;d[q[H=T=1]=s]=1; W(H<=T&&!d[t]) for(i=lnk[k=q[H++]];i;i=e[i].nxt) !d[e[i].to]&&e[i].F&&(d[q[++T]=e[i].to]=d[k]+1);return d[t];} I int DFS(CI x=s,RI f=INF) {if(x==t||!f) return f;RI i,o,g=0;for(i=cur[x];i&&f;i=e[i].nxt) (d[x]+1)==d[e[i].to]&&(o=DFS(e[i].to,min(f,e[i].F)),e[i].F-=o,e[i^1].F+=o,g+=o,f-=o);return cur[x]=i,g;} I int MaxFlow() {RI g=0;W(BFS()) memcpy(cur,lnk,sizeof(lnk)),g+=DFS();return g;}//最小割=最大流 } class MinCutTree { private: #define add(x,y,z) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y,e[ee].v=z) int ee,lnk[N+5];struct edge {int to,nxt,v;}e[2*N+5]; int id[N+5],idl[N+5],idr[N+5];I void Solve(CI l,CI r)//建最小割樹 { RI i,v,tl=0,tr=0;if(l>=r) return;D::Init(),D::s=id[l],D::t=id[r],v=D::MaxFlow();//任取兩點求一組最小割 for(add(id[l],id[r],v),add(id[r],id[l],v),i=l;i<=r;++i) (D::d[id[i]]?idl[++tl]:idr[++tr])=id[i];//最小割樹上連邊;根據最小割劃分點集 for(i=1;i<=tl;++i) id[l+i-1]=idl[i];for(i=1;i<=tr;++i) id[l+tl+i-1]=idr[i];Solve(l,l+tl-1),Solve(l+tl,r);//遞迴構造 } int d[N+5],f[N+5][LN+1],g[N+5][LN+1];I void dfs(CI x)//預處理 { RI i;for(i=1;i<=LN;++i) f[x][i]=f[f[x][i-1]][i-1],g[x][i]=min(g[x][i-1],g[f[x][i-1]][i-1]);//倍增預處理 for(i=lnk[x];i;i=e[i].nxt) e[i].to^f[x][0]&&(d[e[i].to]=d[f[e[i].to][0]=x]+1,g[e[i].to][0]=e[i].v,dfs(e[i].to),0); } public: I void Build() {for(RI i=1;i<=n;++i) id[i]=i;Solve(1,n),dfs(1);} I int Q(RI x,RI y)//樹上倍增 { #define U(x,i) (t=min(t,g[x][i]),x=f[x][i]) RI i,t=INF;for(d[x]<d[y]&&(swap(x,y),0),i=0;d[x]^d[y];++i) (d[x]^d[y])>>i&1&&U(x,i); if(x==y) return t;for(i=LN;~i;--i) f[x][i]^f[y][i]&&(U(x,i),U(y,i));return U(x,0),U(y,0),t; } }T; int main() { RI i,x,y,z;for(read(n,m),i=1;i<=m;++i) read(x,y,z),D::Add(x,y,z); T.Build();RI Qt;read(Qt);W(Qt--) read(x,y),writeln(T.Q(x,y));return clear(),0; }