最小割树和网络流水题

Posted Harris-H

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最小割树和网络流水题相关的知识,希望对你有一定的参考价值。

最小割树和网络流水题

0.前置知识

最小割树,也叫 G o m o r y − H u   T r e e Gomory-Hu\\ Tree GomoryHu Tree

用来解决无向图最小割的问题。



1.构造最小割树

分治+最小割构造

对于当前点集合区间 [ l , r ] [l,r] [l,r]

  • 从当前集合任意选择两个结点 s t , e d st,ed st,ed,求 ( s t , e d ) (st,ed) (st,ed)
  • 然后建立最小割树的边权 ( s t , e d , w ) (st,ed,w) (st,ed,w) w w w ( s t , e d ) (st,ed) (st,ed)的最小割。
  • 然后根据最小割后的残余网络, d e p [ i ] ≠ 0 dep[i]\\ne 0 dep[i]=0的点加入一个集合,该集合是 s t st st可以访问的,而 d e p [ i ] = 0 dep[i]=0 dep[i]=0则是 s t st st访问不到的集合,是另一个集合。
  • 然后递归解决这两个集合。
  • 递归的终止条件是集合大小为 1 1 1,即 l = r l=r l=r
  • 因为是分治解决,跑 n n n d i n i c dinic dinic,时间复杂度: O ( n 3 m ) O(n^3m) O(n3m),但很难跑满。
void divi(int l,int r){
	if(l==r) return;// 递归终止条件: 集合的大小为1.
	st=t[l],ed=t[l+1]; //任意选择集合中的两个结点st,ed
	ll w=dinic(); //求最小割w
	add1(st,ed,w);// 最小割树建立边权edge(st,ed,w)
	int c1=0,c2=0;//分治解决两个集合.
	for(int i=l;i<=r;i++)
		if(dep[t[i]]) t1[++c1]=t[i]; //集合1,st可以访问到的结点/
		else t2[++c2]=t[i];	//st访问不到的结点.
	for(int i=l;i<l+c1;i++) t[i]=t1[i-l+1];
	for(int i=l+c1;i<=r;i++) t[i]=t2[i-l-c1+1];
	divi(l,l+c1-1);//分治
	divi(l+c1,r);
}

2.最小割树的性质

为了方便,令 λ ( u , v ) \\lambda(u,v) λ(u,v) u , v u,v u,v的最小割。

  • 原图的 λ ( u , v ) = m i n { w e d g e ( i , j ) } ,   e d g e ( i , j ) \\lambda(u,v)=min\\{w_{edge}(i,j)\\},\\ edge(i,j) λ(u,v)=min{wedge(i,j)}, edge(i,j) u u u v v v路径上的边权。即答案就是树上 u , v u,v u,v两点路径的最小边权。
  • 求树上两点路径最小边权,可以用倍增处理。

3.习题

P4897 【模板】最小割树

// Problem: P4897 【模板】最小割树(Gomory-Hu Tree)
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P4897
// Memory Limit: 125 MB
// Time Limit: 500 ms
// Date: 2021-07-21 20:40:49
// --------by Herio--------

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull; 
const int N=505,M=6e3+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb emplace_back
#define SZ(a) (int)a.size()
#define ios ios::sync_with_stdio(false),cin.tie(0) 
void Print(int *a,int n){
	for(int i=1;i<n;i++)
		printf("%d ",a[i]);
	printf("%d\\n",a[n]); 
}
//Dinic O(n^2m)
int n,m,st,ed;
int id(int x,int y){
	return (x-1)*n+y;
}
struct edge{
	int to,nt,w;
}e[M],g[M];
int h[N],cur[N],cnt=1,dep[N],cnt1,h1[N];
void add(int u,int v,int w){
	e[++cnt]={v,h[u],w},h[u]=cnt;
	e[++cnt]={u,h[v],0},h[v]=cnt;
}
void add1(int u,int v,int w){
	g[++cnt1]={v,h1[u],w},h1[u]=cnt1;
	g[++cnt1]={u,h1[v],w},h1[v]=cnt1;	
}
int dfs(int u,int c){	//search for augment path
	if(u==ed) return c;
	int res=c;
	for(int &i=cur[u];i;i=e[i].nt){
		int v=e[i].to,w=e[i].w;
		if(w&&dep[v]==dep[u]+1){
			int now=dfs(v,min(res,w));
			if(!now) dep[v]=1;
			else e[i].w-=now,e[i^1].w+=now,res-=now;
		}
		if(!res) return c;
	}return c-res;
} 
bool bfs(){		//layer the graph
	queue<int>q;q.push(st);mst(dep,0),dep[st]=1;
	while(!q.empty()){
		int u=q.front();q.pop();cur[u]=h[u];
		for(int i=h[u];i;i=e[i].nt){
			int v=e[i].to,w=e[i].w;
			if(w&&!dep[v]) dep[v]=dep[u]+1,q.push(v);
		}
	}return dep[ed];
} 
ll dinic(){
	for(int i=2;i<=cnt;i+=2) e[i].w=(e[i].w+e[i^1].w),e[i^1].w=0;
	ll s=0;
	while(bfs()) s+=dfs(st,inf);
	return s;
}
int t[N],t1[N],t2[N];

void divi(int l,int r){
	if(l==r) return;// 递归终止条件: 集合的大小为1.
	st=t[l],ed=t[l+1]; //任意选择集合中的两个结点st,ed
	ll w=dinic(); //求最小割w
	add1(st,ed,w);// 最小割树建立边权edge(st,ed,w)
	int c1=0,c2=0;//分治解决两个集合.
	for(int i=l;i<=r;i++)
		if(dep[t[i]]) t1[++c1]=t[i]; //集合1,st可以访问到的结点/
		else t2[++c2]=t[i];	//st访问不到的结点.
	for(int i=l;i<l+c1;i++) t[i]=t1[i-l+1];
	for(int i=l+c1;i<=r;i++) t[i]=t2[i-l-c1+1];
	divi(l,l+c1-1);//分治
	divi(l+c1,r);
}
//倍增LCA
int f[N][21];
int mn[N][21];
void dfs(int u){
	for(int i=h1[u];i;i=g[i].nt){
		int v=g[i].to;
		if(v!=f[u][0]){
			f[v][0]=u;
			mn[v][0]=g[i].w;
			dep[v]=dep[u]+1;
			dfs(v);
		}
	}
}
void init(){
	for(int j=1;j<=10;j++)
		for(int i=1;i<=n;i++)
		{
			f[i][j]=f[f[i][j-1]][j-1];
			mn[i][j]=min(mn[i][j-1],mn[f[i][j-1]][j-1]);
		}
}
int query(int u,int v){
	if(dep[u]<dep[v]) swap(u,v);
	int delta=dep[u]-dep[v];
	int ans=inf;
	for(int i=0;i<=10;i++)
		if(delta>>i&1){
			ans=min(ans,mn[u][i]);
			 u=f[u][i];
		}
	if(u==v) return ans;
	for(int i=10;~i;i--){
		if(f[u][i]!=f[v][i]){
			ans=min(ans,mn[u][i]);
			ans=min(ans,mn[v][i]);
			u=f[u][i],v=f[v][i];
		网络战争 [KD-Tree+最小割树]

[ZJOI2011] 最小割 - 最小割树

[XSY 1145] 网络战争 平面最近点对 最小割树

最小割树小记

最小割树 - CQOI2016不同的最小割

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