感觉大家写的都是递归构造的方法,其实非递归构造写法更好理解,写起来也更简单

关于递归构造与非递归构造王文涛论文中都有详细的证明与讲解

最小割树存在的主要原因就是这个定理:

接下来补充一下论文中另外一个重要定理的证明:

如图:

我们把它们按顺序连起来

我们可以看出,这些点对 分为两类,一类是跨过u,v最小割的点对,一类是同侧点对,并且必定有一组跨过最小割的点对(因为最坏情况是所有点都在同侧,但是这样依然会有一对点(u,w1)或(wk,v)会跨过最小割)

设这些点对(x,y)的权值为\lambda(x,y),那么跨过最小割的点对的\lambda(x,y)一定满足:\lambda(x,y)\leq \lambda(u,v)

因为它们在异侧,所以最坏情况就是割掉u,v最小割

而同侧的点对即便全部的权值都大于\lambda(u,v),也一定会有一组跨过最小割的点对来保障它们的min值是小于等于\lambda(u,v)

(可能会有不严谨的地方,希望大佬不吝赐教)

存个模板:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int gi()
{
	char c;int num=0,flg=1;
	while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
	while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
	return num*flg;
}
#define N 1005
#define M 200005
const int INF=0x3f3f3f3f;
int n,m;
int fir[N],to[M],nxt[M],cap[M],cnt;
void adde(int a,int b,int c1,int c2)
{
	to[++cnt]=b;nxt[cnt]=fir[a];fir[a]=cnt;cap[cnt]=c1;
	to[++cnt]=a;nxt[cnt]=fir[b];fir[b]=cnt;cap[cnt]=c2;
}
int S,T,flow,d[N],vd[N];
int sap(int u,int aug)
{
	if(u==T) return aug;
	int tmp,ret=0,mind=n-1;
	for(int v,p=fir[u];p;p=nxt[p]){
		v=to[p];
		if(cap[p]>0){
			if(d[u]==d[v]+1){
				tmp=sap(v,min(cap[p],aug));
				aug-=tmp;cap[p]-=tmp;
				ret+=tmp;cap[p^1]+=tmp;
				if(d[S]>=n) return ret;
				if(aug==0) break;
			}
			mind=min(mind,d[v]);
		}
	}
	if(ret==0){
		vd[d[u]]--;
		if(!vd[d[u]])
			d[S]=n;
		d[u]=mind+1;
		vd[d[u]]++;
	}
	return ret;
}
bool vs[N];
void dfs(int u)
{
	vs[u]=1;
	for(int v,p=fir[u];p;p=nxt[p]){
		v=to[p];
		if(cap[p]>0&&!vs[v])
			dfs(v);
	}
}
void f()
{
	memset(d,0,sizeof(d));
	memset(vs,0,sizeof(vs));
	memset(vd,0,sizeof(vd));
	vd[0]=n;flow=0;
	while(d[S]<n)
		flow+=sap(S,INF);
	dfs(S);
}
struct node{
	int u,v,c;
}e[M];
void build(int s,int t)
{
	memset(fir,0,sizeof(fir));cnt=1;
	for(int i=1;i<=m;i++)
		adde(e[i].u,e[i].v,e[i].c,e[i].c);
	S=s;T=t;
}
int fa[N],val[N],ans[N][N];
void dfs2(int u,int s,int mi)
{
	ans[s][u]=mi;
	for(int v,p=fir[u];p;p=nxt[p]){
		v=to[p];
		if(!ans[s][v])
			dfs2(v,s,min(mi,cap[p]));
	}
}
int main()
{
	int i,j,Q,u,v;
	n=gi();m=gi();
	for(i=1;i<=m;i++){e[i].u=gi();e[i].v=gi();e[i].c=gi();}
	for(i=2;i<=n;i++)fa[i]=1;
	for(u=2;u<=n;u++){//非递归构造,依次求出每个点与父亲的最小割
		v=fa[u];
		build(u,v);f();
		for(j=1;j<=n;j++)//把与自己同集合的兄弟作为自己的儿子
			if(j!=u&&fa[j]==v&&vs[j])
				fa[j]=u;
		if(vs[fa[v]]){//如果父亲的父亲也与自己同集合,就交换父亲与自己的位置,边权不变,点的编号改变(删掉这一句就是等价流树)
			fa[u]=fa[v];val[u]=val[v];
			fa[v]=u;val[v]=flow;
		}
		else{fa[u]=v;val[u]=flow;}
	}
	memset(fir,0,sizeof(fir));cnt=1;
	for(i=2;i<=n;i++)
		adde(fa[i],i,val[i],val[i]);
	for(i=1;i<=n;i++)
		dfs2(i,i,INF);
	Q=gi();
	for(i=1;i<=Q;i++){
		u=gi();v=gi();
		printf("%d\n",ans[u][v]);
	}
}