感觉大家写的都是递归构造的方法,其实非递归构造写法更好理解,写起来也更简单
关于递归构造与非递归构造王文涛论文中都有详细的证明与讲解
最小割树存在的主要原因就是这个定理:
接下来补充一下论文中另外一个重要定理的证明:
如图:
我们把它们按顺序连起来
我们可以看出,这些点对 分为两类,一类是跨过u,v最小割的点对,一类是同侧点对,并且必定有一组跨过最小割的点对(因为最坏情况是所有点都在同侧,但是这样依然会有一对点(u,w1)或(wk,v)会跨过最小割)
设这些点对(x,y)的权值为,那么跨过最小割的点对的
一定满足:
因为它们在异侧,所以最坏情况就是割掉u,v最小割
而同侧的点对即便全部的权值都大于,也一定会有一组跨过最小割的点对来保障它们的min值是小于等于
(可能会有不严谨的地方,希望大佬不吝赐教)
存个模板:
#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]);
}
}