【模板】最小割树(Gomory-Hu Tree)

传送门

Description

给定一个\(n\)个点\(m\)条边的无向连通图,多次询问两点之间的最小割

两点间的最小割是这样定义的:原图的每条边有一个割断它的代价,你需要用最小的代价使得这两个点不连通

Solution

对于一张无向图,如果 \(s \rightarrow t\) 的最大流是 \(f\),\(s\), \(t\) 所在的割集为 \(S\), \(T\),那么 \(\forall_{x \in S, y \in T}\), \(\operatorname{maxflow}(x \rightarrow y) = f\)

根据以上性质,我们考虑递归求解

构造集合 \(G\) 的最小割树:

  • 任意选两个点,求出最小割,在两个点之间连权值为最小割的边
  • 对两个割集分别求解

\(Wa\)了数十次

以后用static的时候还是注意点

Code?

#include<bits/stdc++.h>
#define ll long long
#define reg register
#define ri reg int i
using namespace std;

inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}

const int MN=505,MM=1505,inf=1e9;

struct edge{int to,w,nex;}e[MM<<1],E[MN<<1];int cur[MN],Hr[MN],hr[MN],En=1,en=1;
void ins(int x,int y,int w,int *h,edge *ed,int &tt)
{
    ed[++tt]=(edge){y,w,h[x]},h[x]=tt;
    ed[++tt]=(edge){x,w,h[y]},h[y]=tt;
}

int d[MN],q[MN],top;

bool bfs(int S,int T)
{
    memset(d,0,sizeof d);
    reg int i,j;
    for(d[q[i=top=1]=S]=1;i<=top;++i)
        for(j=hr[q[i]];j;j=e[j].nex)
            if(e[j].w&&!d[e[j].to])
                d[q[++top]=e[j].to]=d[q[i]]+1;
    return d[T];
}

int dfs(int x,int T,int f)
{
    if(x==T) return f;
    int used=0;
    for(int &i=cur[x];i;i=e[i].nex)
    if(e[i].w&&d[e[i].to]==d[x]+1)
    {
        int tmp=dfs(e[i].to,T,min(f-used,e[i].w));
        e[i].w-=tmp,e[i^1].w+=tmp;used+=tmp;
        if(f==used) return f;
    }
    return d[x]=-1,used;
}

void clear(){for(reg int i=2;i<en;i+=2)e[i].w=e[i^1].w=(e[i].w+e[i^1].w)>>1;}

int dinic(int S,int T)
{
    int maxflow=0;clear();
    while(bfs(S,T))
    {
        memcpy(cur,hr,sizeof cur);
        maxflow+=dfs(S,T,inf);
    }
    return maxflow;
}

void Build(int *id, int n)
{
    if(n==1) return;
    static int s[MN],t[MN];int cnts=0,cntt=0;
    int cut=dinic(id[0],id[1]);
    ins(id[0],id[1],cut,Hr,E,En);
    for(reg int i=0;i<n;++i)
        if(d[id[i]]) s[cnts++]=id[i];
        else t[cntt++]=id[i];
    memcpy(id,s,cnts<<2);
    memcpy(id+cnts,t,cntt<<2);
    if(cnts) Build(id,cnts);
    if(cntt) Build(id+cnts,cntt);
}

int n,dep[MN],fa[MN][15],path[MN][15];

void init_dfs(int x,int f)
{
    for(ri=Hr[x];i;i=E[i].nex)if(E[i].to!=f)
        dep[E[i].to]=dep[x]+1,fa[E[i].to][0]=x,path[E[i].to][0]=E[i].w,init_dfs(E[i].to,x);
}

void init()
{
    dep[1]=1;init_dfs(1,-1);reg int i,j;
    for(i=1;i<10;++i)for(j=1;j<=n;++j)
    {
        fa[j][i]=fa[fa[j][i-1]][i-1];
        path[j][i]=min(path[j][i-1],path[fa[j][i-1]][i-1]);

    }
}

int Min(int x,int y)
{
    reg int ret=1e9;
    if(dep[x]>dep[y]) std::swap(x,y);
    reg int i,c=dep[y]-dep[x];
    for(i=8;~i;--i) if(c>>i&1)
    {
        ret=min(ret,path[y][i]),y=fa[y][i];
    }
    if(x==y) return ret;
    if(dep[x]!=dep[y]) return -1;
    for(i=8;~i;--i)if(fa[x][i]!=fa[y][i])
    {
        ret=min(ret,min(path[x][i],path[y][i])),x=fa[x][i],y=fa[y][i];
    }
    return min(ret,min(path[x][0],path[y][0]));
}

int id[MN];
int main()
{
    reg int i,m,q,x,y;
    n=read(),m=read();
    while(m--) x=read(),y=read(),ins(x,y,read(),hr,e,en);

    for(i=1;i<=n;++i) id[i-1]=i;Build(id,n);init();

    q=read();

    while(q--)
    {
        x=read();y=read();
        printf("%d\n",Min(x,y));
    }
    return 0;
}


Blog来自PaperCloud,未经允许,请勿转载,TKS!

原文地址:https://www.cnblogs.com/PaperCloud/p/10674424.html

时间: 2024-08-01 05:32:41

【模板】最小割树(Gomory-Hu Tree)的相关文章

[ZJOI2011]最小割(最小割树模板)

https://www.luogu.com.cn/problem/P3329 最小割树的用处不仅是做这些裸题,了解这个定理,会对一类问题有更深的思考. 最小割树的实现: 每次取两个点u,v,求它们的割,并在最小割树上给它们连边,权值为这个割. 然后按照S能走到的和能走到T的,分成两类点,继续递归建树. 原图中的两个点的最小割,即为树上边权的最小值. 容易证明最小割<=树上边权的最小值,但是要证明恰好是,比较困难,博主暂时不会. Code: #include<bits/stdc++.h>

【BZOJ-2229】最小割 最小割树(最大流+分治)

2229: [Zjoi2011]最小割 Time Limit: 10 Sec  Memory Limit: 259 MBSubmit: 1565  Solved: 560[Submit][Status][Discuss] Description 小白在图论课上学到了一个新的概念——最小割,下课后小白在笔记本上写下了如下这段话: “对于一个图,某个对图中结点的划分将图中所有结点分成两个部分,如果结点s,t不在同一个部分中,则称这个划分是关于s,t的割. 对于带权图来说,将所有顶点处在不同部分的边的

[ZJOI2011] 最小割 - 最小割树

最小割树裸题 建树后,以每个点为根跑DFS求出距离矩阵,然后暴力回答询问即可 #include <bits/stdc++.h> using namespace std; #define int long long const int maxn=6e2; const int maxm=4e4; const int inf=1e13; int n,m,q; //for the target graph vector <pair<int,int> > g[maxn]; voi

【BZOJ-4435】Juice Junctions 最小割树(分治+最小割)+Hash

4435: [Cerc2015]Juice Junctions Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 20  Solved: 11[Submit][Status][Discuss] Description 你被雇佣升级一个旧果汁加工厂的橙汁运输系统.系统有管道和节点构成.每条管道都是双向的,且每条管道的流量都是1升每秒.管道可能连接节点,每个节点最多可以连接3条管道.节点的流量是无限的.节点用整数1到n来表示.在升级系统之前,你需要对现有

bzoj2229: [Zjoi2011]最小割(分治最小割+最小割树思想)

2229: [Zjoi2011]最小割 题目:传送门 题解: 一道非常好的题目啊!!! 蒟蒻的想法:暴力枚举点对跑最小割记录...绝对爆炸啊.... 开始怀疑是不是题目骗人...难道根本不用网络流???一看路牌....分治最小割?最小割树? 然后开始各种%论文... 简单来说吧,根据各种本蒟蒻不会证明的理论,那么:所有最小割都不是完全独立的,总共有n-1种(也就是树上的n-1条边)最小割 恰好和树的定义一样啊! 那么用一个solve递归函数来解决,一开始任意找两个点作为st和ed来最小割,然后分

bzoj2229: [Zjoi2011]最小割(最小割树)

传送门 这题是用最小割树做的(不明白最小割树是什么的可以去看看这一题->这里) 有了最小割树就很简单了……点数那么少……每次跑出一个最大流就暴力搞一遍就好了 1 //minamoto 2 #include<iostream> 3 #include<cstdio> 4 #include<cstring> 5 #include<algorithm> 6 #include<queue> 7 #define inf 0x3f3f3f3f 8 usi

网络战争 [KD-Tree+最小割树]

题面 思路 首先吐槽一下: 这题是什么东西啊??出题人啊,故意拼题很有意思吗??还拼两个这么毒瘤的东西???? 10K代码了解一下???? 然后是正经东西 首先,本题可以理解为这样: 给定$n$个块,每个块有一个根,每个根只会主动连出去一条无向边,每次求两点最小割 那么,我们显然可以把每个块内的最小割树建立出来,同时把块的根之间的最小割树也建立出来 如果询问点在同一个块里面,显然可以直接最小割树处理 否则就是两边的点到块根的最小割和两个块根之间的最小割的最小值 所以,我们先对于所有的块根,建出K

[2016北京集训试题6]网络战争-[最小割树(网络流)+kd-tree+倍增]

Description A 联邦国有 N 个州,每个州内部都有一个网络系统,有若干条网络线路,连接各个 州内部的城市. 由于 A 国的州与州之间的关系不是太好,每个州都只有首府建立了到别的州的网络.具体来说,每个州的首府都只主动地建立了一条网络线路,连接到距离最近的州的 首府.(欧氏距离.如果有多个,选择标号最小的去连接) B 国探知了 A 国的网络线路分布情况,以及攻陷每条网络线路所需花费的代价,B 国首脑想知道断开 A 国某两个城市之间的网络连接,所需的最少代价.请你计算出来告 诉他. 注:

[bzoj2229][Zjoi2011]最小割_网络流_最小割树

最小割 bzoj-2229 Zjoi-2011 题目大意:题目链接. 注释:略. 想法: 在这里给出最小割树的定义. 最小割树啊,就是这样一棵树.一个图的最小割树满足这棵树上任意两点之间的最小值就是原图中这两点之间的最小割. 这个性质显然是非常优秀的. 我们不妨这样假设,我么已经把最小割树求出来了,那么这个题就迎刃而解了. 我们可以直接枚举点对,然后暴力验证就可以直接枚举出所有的合法点对是吧. 那么问题来了,我们如何才能求出所有的合法的点对? 这就需要用到了最小割树的构建过程. 我们最小割树的构