最小割树的定义: 定义一棵树T为最小割树,如果对于树上的所有边(s,t),树上去掉(s,t)后产生的两个集合恰好是原图上(s,t)的最小割把原图分成的两个集合,且边(u,v)的权值等于原图上(u,v)的最小割。

最小割树的性质:原图上u,v两点最小割就是最小割树上u到v的路径上权值最小的边。


我们要知道,最小割树是对于无向图来说的,有向图不存在此树及性质。

我们可以利用 分治的思想去构造最小割树 ,每次随机选两个点,对此两点跑最小割,之后在最小割树上连边。
然后对于跑完之后的残量网络,上面的点集分成了两个集合,然后分别分治下去即可。(最后一次bfs,达不到终点,即增广数组有些点的值为0)

总复杂度为跑n次最小割,复杂度为 n^3 * m 但是我们要知道,最小割的复杂度远远达不到上限。

之后,剪完最小割树之后,求最小边就可以直接树上倍增,或者树剖了。

例题:最小割树例题


AC代码:

#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
const int N=510,M=1e4+10;
int n,m,q;
struct maxflow{
    int head[N],nex[M],to[M],w[M],h[N],tot=1,s,t;   queue<int> q;  
    inline void ade(int a,int b,int c){
        to[++tot]=b; nex[tot]=head[a]; w[tot]=c; head[a]=tot;
    }
    inline void add(int a,int b,int c){ade(a,b,c);  ade(b,a,0);}
    inline int bfs(){
        q.push(s);  memset(h,0,sizeof h);   h[s]=1;
        while(q.size()){
            int u=q.front();    q.pop();
            for(int i=head[u];i;i=nex[i]){
                if(w[i]&&!h[to[i]]){
                    h[to[i]]=h[u]+1;    q.push(to[i]);
                }
            }
        }
        return h[t];
    }
    int dfs(int x,int f){
        if(x==t)    return f;   int fl=0;
        for(int i=head[x];i&&f;i=nex[i]){
            if(w[i]&&h[to[i]]==h[x]+1){
                int mi=dfs(to[i],min(w[i],f));
                w[i]-=mi,w[i^1]+=mi,fl+=mi,f-=mi;
            }
        }
        if(!fl) h[x]=-1;
        return fl;
    }
    inline void init(){
    	for(int i=2;i<=tot;i+=2)	w[i]+=w[i^1],w[i^1]=0;
	}
    inline int dinic(){
        int res=0;  init();
        while(bfs())    res+=dfs(s,inf);
        return res;
    }
}d;
struct min_cut_tree{
    int head[N],nex[M],to[M],w[M],tot=0;
    inline void ade(int a,int b,int c){
        to[++tot]=b; nex[tot]=head[a]; w[tot]=c; head[a]=tot;
    }
    inline void add(int a,int b,int c){ade(a,b,c);  ade(b,a,0);}
    int a[N],b[N],c[N];
    void build(int l,int r){
        if(l==r)    return ;
        d.s=a[l],d.t=a[l+1];    int flow=d.dinic();
        add(a[l],a[l+1],flow);
        int cnt1=0,cnt2=0;
        for(int i=l;i<=r;i++){
            if(d.h[a[i]])   b[++cnt1]=a[i];
            else    c[++cnt2]=a[i];
        }
        for(int i=l;i<=l+cnt1-1;i++)    a[i]=b[i-l+1];
        for(int i=l+cnt1;i<=r;i++)  a[i]=c[i-cnt1-l+1];
        build(l,l+cnt1-1);  build(l+cnt1,r);
    }
    int h[N],f[N][10],mi[N][10],lg[N];
    void dfs(int x,int fa){
        h[x]=h[fa]+1;
        for(int i=1;(1<<i)<=h[x];i++){
            f[x][i]=f[f[x][i-1]][i-1];
            mi[x][i]=min(mi[x][i-1],mi[f[x][i-1]][i-1]);
        }
        for(int i=head[x];i;i=nex[i]){
            if(to[i]==fa)   continue;
            f[to[i]][0]=x; mi[to[i]][0]=w[i];
            dfs(to[i],x);
        }
    }
    void solve(){
        for(int i=1;i<=n;i++)   lg[i]=lg[i-1]+(1<<lg[i-1]==i);
        for(int i=1;i<=n;i++)   a[i]=i;
        build(1,n); dfs(1,0);
    }
    inline int ask(int x,int y){
        int res=inf;
        if(h[x]<h[y])   swap(x,y);
        while(h[x]>h[y]){
            res=min(res,mi[x][lg[h[x]-h[y]]-1]);
            x=f[x][lg[h[x]-h[y]]-1];
        }
        if(x==y)    return res;
        for(int i=lg[h[x]]-1;i>=0;i--){
            if(f[x][i]!=f[y][i]){
                res=min(res,mi[x][i]); res=min(res,mi[y][i]);
                x=f[x][i],y=f[y][i];
            }
        }
        res=min(res,mi[x][0]); res=min(res,mi[y][0]);
        return res;
    }
}mct;
int main(){
    ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
    cin>>n>>m;
    for(int i=1,a,b,c;i<=m;i++) cin>>a>>b>>c,d.add(a,b,c),d.add(b,a,c);
    mct.solve();
    cin>>q;
    while(q--){
        int a,b;    cin>>a>>b;
        cout<<mct.ask(a,b)<<'\n';
    }
    return 0;
}