最小割树 学习笔记
思路
你最终会得到图的一棵生成树,然后 \(a,b\) 两点的最小割即是生成树上 \(a,b\) 间路径中,权值最小的那条边。
很神奇。那你怎么建出这个树来呢?
首先,最小割等于最大流。你需要一个 \(\text{Dinic}\) 模板。
故此,你在当前的点集中选定随意选定两个点 \(a,b\),求出它们最小割 \(w\),生成树上增加一条边 \(a-b:w\)
此时最小割已经把图分成了两部分。这两部分分别递归,每个部分也是选两个点,跑最大流……(注意每次跑最大流时都要把流量复原)
怎么实现?在 solve(l,r)
中。
设 \(dot\) 为当前部分的点集。一开始 \(dot_i=i\)。
然后思考 \(\text{Dinic}\) 的过程:求出最小割后,还会进行一次 \(\text{bfs}\) 然后返回 \(\text{false}\)。
所以那一次 \(\text{bfs}\) 中,遍历过的点就是一部分,\(dep_o\)会有值;没有遍历过的则全是初值(比如你 memset
的 0 或 -1)。
然后你要一个临时数组 \(dotz\)。设立两个指针:左指针 \(L=l-1\) 和右指针 \(R=r+1\)。
扫一遍 \(dot\)。对于 \(dep[\ dot_i\ ]\) 有值的, 左指针加一,并 \(dotz_L=dot_i\);否则就右指针减一,并 \(dotz_R=dot_i\)。
最后把 \(dotz\) 复制回 \(dot\)。这样点集就分成了两部分: \([l,L],[R,r]\)。分别递归即可。
建出树后,当然是去搞 \(\text{LCA}\) 以及树上\(\text{ST}\)。然后就能 \(O(\log n)\) 求最小割。
吐槽:洛谷除了 曼哈顿计划EX 其它的最小割树题都是板子题……
代码
#include<bits/stdc++.h>
#define rep(i,x,y) for(int i=x;i<=y;++i)
#define per(i,x,y) for(int i=x;i>=y;--i)
#define mar(o,fst,e) for(int E=fst[o];E;E=e[E].nxt)
#define v e[E].to
#define vz ez[E].to
using namespace std;
const int n7=513,m7=3033,inf=INT_MAX-100;
struct dino{int to,nxt,w,w0;}e[m7],ez[m7];
int n,m,T,fst[n7],fstd[n7],ecnt0,ecnt=1,dep[n7],que[n7],ds,dt;
int fstz[n7],dept[n7],fc[n7][13],mni[n7][13],dot[n7],dotz[n7];
int rd(){
int shu=0;char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))shu=(shu<<1)+(shu<<3)+ch-'0',ch=getchar();
return shu;
}
void Dedge(int sta,int edn,int w,dino *eh,int *fsth){
ecnt++;
eh[ecnt]=(dino){edn,fsth[sta],w,w};
fsth[sta]=ecnt;
}
void edge(int sta,int edn,int w,dino *eh,int *fsth){
Dedge(sta,edn,w,eh,fsth),Dedge(edn,sta,w,eh,fsth);
}
bool bfs(){
memset(dep,0,sizeof dep);
int head=1,tail=1;que[1]=ds;
dep[ds]=1,fstd[ds]=fst[ds];
while(head<=tail){
int o=que[head];
mar(o,fst,e){
if(dep[v]||e[E].w==0)continue;
dep[v]=dep[o]+1,fstd[v]=fst[v];
if(v==dt)return 1;
tail++,que[tail]=v;
}
head++;
}
return 0;
}
int dfs(int o,int val){
if(o==dt)return val;
int tot=val;
mar(o,fstd,e){
fstd[o]=E;
if(!tot){dep[o]=inf;break;}
if(dep[v]!=dep[o]+1||e[E].w==0)continue;
int out=dfs(v,min(tot,e[E].w));
e[E].w-=out,e[E^1].w+=out;
tot-=out;
}
return val-tot;
}
int dinic(int p,int q){
ds=p,dt=q;
rep(E,2,ecnt0)e[E].w=e[E].w0;
int tot=0;
while(bfs())tot+=dfs(ds,inf);
return tot;
}
void plant(int l,int r){
if(l==r)return;
edge(dot[l],dot[r],dinic(dot[l],dot[r]),ez,fstz);
int zuo=l-1,you=r+1;
rep(i,l,r){
if(dep[ dot[i] ])zuo++,dotz[zuo]=dot[i];
else you--,dotz[you]=dot[i];
}
rep(i,l,r)dot[i]=dotz[i];
plant(l,zuo),plant(you,r);
}
void dfst(int o){
rep(i,1,9)fc[o][i]=fc[ fc[o][i-1] ][i-1];
rep(i,1,9)mni[o][i]=min(mni[o][i-1],mni[ fc[o][i-1] ][i-1]);
mar(o,fstz,ez){
if(dept[vz])continue;
dept[vz]=dept[o]+1;
fc[vz][0]=o,mni[vz][0]=ez[E].w;
dfst(vz);
}
}
int Dlca(int p,int q){
if(dept[p]<dept[q])p^=q^=p^=q;
int fin=inf;
per(i,9,0){
if(dept[ fc[p][i] ]<dept[q])continue;
fin=min(fin,mni[p][i]);
p=fc[p][i];
}
if(p==q)return fin;
per(i,9,0){
if(fc[p][i]==fc[q][i])continue;
fin=min(fin,min(mni[p][i],mni[q][i]));
p=fc[p][i],q=fc[q][i];
}
fin=min(fin,min(mni[p][0],mni[q][0]));
return fin;
}
int main(){
n=rd(),m=rd();
rep(i,1,n)dot[i]=i;
rep(i,1,m){
int sta=rd(),edn=rd(),w=rd();
edge(sta,edn,w,e,fst);
}
ecnt0=ecnt,ecnt=0;
plant(1,n);
dept[1]=1,dfst(1);
T=rd();
while(T--){
int p=rd(),q=rd();
printf("%d\n",Dlca(p,q));
}
return 0;
}