最小割树 学习笔记

思路

你最终会得到图的一棵生成树,然后 \(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;
}
posted @ 2021-01-04 21:40  BlankAo  阅读(71)  评论(0编辑  收藏  举报