「最小割树 」学习笔记 「P4897 【模板】最小割树(Gomory-Hu Tree)」 题解

news/2025/2/22 14:07:03/文章来源:https://www.cnblogs.com/Charlieljk/p/18729786

最小割树

最小割树就是通过分治建出一棵树,树上两点的最小割就等于原图上的最小割,树上两点路径唯一,其最小割就等于路径上边权的最小值。

建树时,任意选择两点最为 \(s,t\) 跑最小割,求得 \(ans_{s,t}\),并将其分为两个集合 \(S,T\),对于 \(x\in S,y\in T\),有 \(ans_{x,y}=ans_{y,x}=\min(ans_{s,t},ans_{x,s},ans_{t,y})\)

之后对于这两部分再分别跑最小割,重复上述步骤,就能在理论(网络流的复杂度跑不满) \(O(n^3m)\) 的复杂度内建出一棵最小割树。

答案处理

建树

建树就是对于每一层分治将 \(s,t\) 连上边权为 \(ans_{s,t}\) 的边即可,\(ans_{s,t}\) 为本次最小割跑出的答案。

因为原图两点的最小割就等于树上两点路径上边权的最小值,根据这点就可以有很多种处理方式了:

  • \(O(n)\) 预处理,之后 \(ans_{x,y}=\min(ans_{x,lca(x,y)},ans_{lca(x,y),y})\),总体查询复杂度 \(O(Q\log n)\)
  • 树上倍增,查询复杂度也是 \(O(Q\log n)\)
  • 因为 \(n\) 是很小的,所以直接以每个点为根遍历一遍树预处理出答案也是可以的,复杂度 \(O(n^2)\)
  • ……

不建树

根据上面的式子,对于 \(x\in S,y\in T\),有 \(ans_{x,y}=ans_{y,x}=\min(ans_{s,t},ans_{x,s},ans_{t,y})\)

根据这个式子,在分治的同时处理出答案即可,不需要把树建出来。

一些细节

  • 每一次跑最小割前要将所有边恢复初始状态。
  • 本题为无向图,要所以可以不用对于每一个边再建一条流量为 \(0\) 的反边了,建双向边即可。
  • 注意分治时每层的 \(s,t\) 不同,根据需要保存本层的 \(s,t\)
  • 因为数据有误,给定图的真实点数应该是 \(n+1\) 个,编号为 \(0\)\(n\)

代码如下

使用的时不把树建出来的方法。

#include<bits/stdc++.h>
#define ll long long
#define mkp make_pair
using namespace std;
const int N=510,M=3010,inf=0x3f3f3f3f;
#ifdef __linux__
#define gc getchar_unlocked
#define pc putchar_unlocked
#else
#define gc _getchar_nolock
#define pc _putchar_nolock
#endif
inline bool blank(const char x) {return !(x^32)||!(x^10)||!(x^13)||!(x^9);}
template<typename Tp> inline void read(Tp &x) {x=0; register bool z=true; register char a=gc(); for(;!isdigit(a);a=gc()) if(a=='-') z=false; for(;isdigit(a);a=gc()) x=(x<<1)+(x<<3)+(a^48); x=(z?x:~x+1);}
inline void read(double &x) {x=0.0; register bool z=true; register double y=0.1; register char a=gc(); for(;!isdigit(a);a=gc()) if(a=='-') z=false; for(;isdigit(a);a=gc()) x=x*10+(a^48); if(a!='.') return x=z?x:-x,void(); for(a=gc();isdigit(a);a=gc(),y/=10) x+=y*(a^48); x=(z?x:-x);}
inline void read(char &x) {for(x=gc();blank(x)&&(x^-1);x=gc());}
inline void read(char *x) {register char a=gc(); for(;blank(a)&&(a^-1);a=gc()); for(;!blank(a)&&(a^-1);a=gc()) *x++=a; *x=0;}
inline void read(string &x) {x=""; register char a=gc(); for(;blank(a)&&(a^-1);a=gc()); for(;!blank(a)&&(a^-1);a=gc()) x+=a;}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y) {read(x),read(y...);}
template<typename Tp> inline void write(Tp x) {if(!x) return pc(48),void(); if(x<0) pc('-'),x=~x+1; register int len=0; register char tmp[64]; for(;x;x/=10) tmp[++len]=x%10+48; while(len) pc(tmp[len--]);}
inline void write(const double x) {register int a=6; register double b=x,c=b; if(b<0) pc('-'),b=-b,c=-c; register double y=5*powl(10,-a-1); b+=y,c+=y; register int len=0; register char tmp[64]; if(b<1) pc(48); else for(;b>=1;b/=10) tmp[++len]=floor(b)-floor(b/10)*10+48; while(len) pc(tmp[len--]); pc('.'); for(c*=10;a;a--,c*=10) pc(floor(c)-floor(c/10)*10+48);}
inline void write(const pair<int,double>x) {register int a=x.first; if(a<7) {register double b=x.second,c=b; if(b<0) pc('-'),b=-b,c=-c; register double y=5*powl(10,-a-1); b+=y,c+=y; register int len=0; register char tmp[64]; if(b<1) pc(48); else for(;b>=1;b/=10) tmp[++len]=floor(b)-floor(b/10)*10+48; while(len) pc(tmp[len--]); a&&(pc('.')); for(c*=10;a;a--,c*=10) pc(floor(c)-floor(c/10)*10+48);} else cout<<fixed<<setprecision(a)<<x.second;}
inline void write(const char x) {pc(x);}
inline void write(const bool x) {pc(x?49:48);}
inline void write(char *x) {fputs(x,stdout);}
inline void write(const char *x) {fputs(x,stdout);}
inline void write(const string &x) {fputs(x.c_str(),stdout);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y) {write(x),write(y...);}
int n,m,s,t,cs[N],ct[N],dep[N],node[N],ans[N][N];
int tot=1,now[N],head[N],nxt[M],to[M],w[M];
inline void add(int x,int y,int z)
{nxt[++tot]=head[x],to[head[x]=tot]=y,w[tot]=z;nxt[++tot]=head[y],to[head[y]=tot]=x,w[tot]=z;
}
inline bool bfs()
{queue<int>q; memset(dep,0,sizeof(dep));for(memcpy(now,head,sizeof(now)),q.push(s),dep[s]=1;!q.empty();){int x=q.front(); q.pop();for(int i=head[x],y;y=to[i];i=nxt[i]) if(w[i]&&!dep[y]){dep[y]=dep[x]+1,q.push(y); if(y==t) return true;}} return false;
}
inline int dfs(int x,int sum)
{if(x==t||!sum) return sum; int sur,res=0;for(int &i=now[x],y;y=to[i];i=nxt[i]) if(w[i]&&dep[y]==dep[x]+1){if(!(sur=dfs(y,min(sum,w[i])))) {dep[y]=-1; continue;}w[i]-=sur,w[i^1]+=sur,res+=sur; if(!(sum-=sur)) break;} return res;
}
inline void build(int l,int r)
{if(l>=r) return ; s=node[l],t=node[r]; int res=0,ns=0,nt=0;for(int i=2;i<tot;i+=2) w[i]=w[i^1]=w[i]+w[i^1]>>1;while(bfs()) res+=dfs(s,inf); ans[s][t]=ans[t][s]=res;for(int i=l,x;i<=r;i++) dep[x=node[i]]?cs[++ns]=x:ct[++nt]=x;for(int i=1;i<=ns;i++) node[l+i-1]=cs[i];for(int i=1;i<=nt;i++) node[l+ns+i-1]=ct[i];int os=s,ot=t; build(l,l+ns-1),build(l+ns,r); for(int i=1,j,x,y;i<=ns;i++) for(x=node[l+i-1],j=1;j<=nt;j++)y=node[l+ns+j-1],ans[x][y]=ans[y][x]=min({ans[x][os],ans[ot][y],res});
}
signed main()
{read(n,m),n++,memset(ans,0x3f,sizeof(ans));for(int x,y,z;m--;) read(x,y,z),add(x+1,y+1,z);for(int i=1;i<=n;i++) node[i]=i; build(1,n),read(m);for(int x,y;m--;) read(x,y),write(ans[x+1][y+1],'\n');
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/887991.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

用一个静态图片实现出怪路线提示

用一个静态图片实现下面的效果

Windows平台调试器原理与编写01.调试框架

调试器原理与编写01.调试框架-C/C++基础-断点社区-专业的老牌游戏安全技术交流社区 - BpSend.net 调试框架 调试器最基本功能: 断点,单步 断点分为三类软件断点 硬件断点 内存断点window提供了一套机制,帮助用户来实现一套3环的调试器 事件驱动 : 窗口的各种操作(外在的想要对窗…

本地部署DeepSeek-R1-AWQ

一、部署环境准备 系统信息:主机名为 10-200-3-23 IP 地址为 10.200.3.23 操作系统为 ubuntu 22.04 配备 8 卡 A100。二、驱动与桥接器安装安装 gcc执行命令 apt-get update -y apt install build-essential -y安装驱动下载驱动 wget https://us.download.nvidia.com/tesla/…

k8s部署nfs+sc

1.下载软件包nfs-subdir-external-provisioner-4.0.18.tgz 该软件包内文件截图 修改values.yaml 在目录内执行 helm install nfs-provisioner -f values.yaml . #老重要了2.查看执行是否成功 3.测试是否成功 编写yaml apiVersion: v1kind: PersistentVolumeClaimmetadata…

P2661 [NOIP 2015 提高组] 信息传递——染色做法

原题 本来想当水题刷的,结果被水题刷了。。。70到80到90到100,必须写个题解记录一下(doge) 题目分析一句话:求一个无权有向图中的最短环路(确保有环) tip:每一个点出度为一,那么必然有环,以样例为例如下。思路没必要每轮模拟全部的传送,只看某一个人的传送过程: 就…

有哪些好用的AI工具?(你想要的AI工具都在这)

1. 常见应用场景 1.1. 国内通用大模型模型名称 简介 官网地址DeepSeek 深度求索公司研发的高性能开源模型,以低成本、高推理能力著称,支持数学、代码等复杂任务。 https://chat.deepseek.com/豆包 字节跳动开发的智能语言模型,基于深度学习技术,支持多种自然语言处理任务。…

Kubernetes 集群上部署 Open WebUI

在前一篇博文中记录了 k8s 集群上部署 ollama + deepseek-r1:7b,这篇博文记录一下 Open WebUI 的部署。还是用 helm 部署,添加 open-webui 的 helm repo,准备 helm 清单文件,通过 helm 命令在 ai 命名空间下进行部署在前一篇博文中记录了 k8s 集群上部署 ollama + deepseek…

2025.2.22

Hehe_0 模拟赛内容随笔 [NOIP2015]金币1 2 3 4 。。。1 2-3 4-6 7-10 。。。观察数据范围1e4,暴力就行,然后可以去思考优化版本 由于已经把规律给出来了,所以可以提前离线处理出来,如果数据过大,可以预处理每一次金币变化的天数,然后根据提问二分找区间然后求和。这种数…

充电桩功能扩展,解决桩企内存不足的问题

OCPP(开放充电点协议)1.6是电动汽车充电基础设施中广泛使用的通信标准。尽管OCPP 1.6为充电桩与中央管理系统(CSMS)之间的交互提供了基本功能,但由于OCPP主板的内存资源有限,其能够实现的功能也受到了一定的限制。为了解决这一问题,OCPP协议网关作为OCPP主板的扩展,能够…

Spring复习-AOP

AOP的概念 AOP,Aspect Oriented Programming,面向切面编程,是对面向对象编程OOP的升华。OOP是纵向对一个事物的抽象,一个对象包括静态的属性信息,包括动态的方法信息等。而AOP是横向的对不同事物的抽象,属性与属性、方法与方法、对象与对象都可以组成一个切面,而用这种思…

Univer sheet加载上下文菜单卡死崩溃问题定位

背景: 我的应用技术栈是Vue3,Univer是基于react的,所以定位问题花了很久,在此记录一下查问题的方式。 使用Chrome DevTools的Performance进行录制,复现卡死操作后,在Performance Monitr中可以看到CPU Usage持续100%,且页面重计算次数持续飙高。由于页面卡死,Performanc…

乐园杂音

沟槽的杉井光为什么还不填坑! 其实兔子最早看的一批轻小说就有乐杂,但是那时候没有写读后感的习惯,现在重刷一遍乐杂,就顺便写一下读后感。兔子每次给别人看这张图都会让他们猜女主是谁。 其实吧,虽然普遍认为《离别的钢琴奏鸣曲》比《乐园杂音》写的要好,但是兔子更喜欢…