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

题意:

戳这里

分析:

  • 前置芝士:最小割

简单证明几个引理 :

  1. 我们记 \(\delta(x,y)\) 表示 \((x,y)\) 之间的最小割代价, \(\forall \ p\in V_x,\ q\in V_y,\delta(x,y)\ge \delta(p,q)\)

证明: 反证,若 \(\delta(p,q)<\delta(x,y)\) 那么割断 \(p,q\) 的代价不足以割断 \(x,y\)\(p,q\) 分别和 \(x,y\) 相连

  1. 任意三个点 \(a,b,c\) 存在 \(\delta(a,b)\ge min(\delta(a,b),\delta(a,c),\delta(b,c))\)

证明: 我们令 $\delta (a,b) $ 为最小的一对,那么割掉 \(a\to b\) 之后我们假设 \(c\)\(b\) 相连 ,那么由上面的引理可得 \(\delta(a,c)\le \delta(a,b)\) 又因为 \(\delta(a,b)\le \delta(a,c)\) 那么 \(\delta(a,b)=\delta(a,c)\)

  1. 对于任意不同的两点u,v, \(\delta(u,v) \geq min(\delta(u,w_1),\delta(w_1,w_2),\delta(w_2,w_3) \dots , \delta(w_k,v))\)

  2. 对于任意不同的两点 \(u,v\) ,令 \(p,q\) 为最小割树x到y路径上的两点,且 \(\delta(p,q)\) 最小,那么 \(\delta(u,v)=\delta(p,q)\) .也就是说,u,v 两点最小割就是最小割树上u到v的路径上权值最小的边

证明: 由 \(1\)\(\delta(u,v)\ge \delta(p,q)\) 又因为 \(\delta(p,q)\le \delta(u,v)\) 那么 \(\delta(u,v)=\delta(p,q)\)


好了,有了上面里的定理,我们就可以开始构造最小割树了,我们每次找到两个点,求他们的最小割,然后按照与这两个点的联通性划分成两个点集,递归处理 , 这样每次跑了 \(n\) 次最大流,每一次的复杂度 \(n^2m\) 总体复杂度 \(O(n^3m)\)\(1e11\) 呀真就过了/EE

tip:

  1. 虽然每一次划分成了两个点集,但是求最小割还是用的是全局的图,而且我们每次得还原之前更新的流量
  2. 同上,虽然固定了源汇,但是所有的点的信息都需要更新(因为有的点的 \(dep\) 没有更新,我调了半个多小时)

代码:

#include<bits/stdc++.h>
#define pii pair<int,int>
#define mk(x,y) make_pair(x,y)
#define lc rt<<1
#define rc rt<<1|1
#define pb push_back
#define fir first
#define sec second

using namespace std;

namespace zzc
{
	inline int read()
	{
		int x=0,f=1;char ch=getchar();
		while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
		while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
		return x*f;
	}
	
	const int maxn = 505;
	const int maxm = 1505;
	const int inf = 0x3f3f3f3f;
	int n,m,qt;
	
	namespace network
	{
		int cnt=1,st,ed;
		int head[maxn],dep[maxn],cur[maxn];
		struct edge
		{
			int to,nxt,w;
		}e[maxm<<1];
		queue<int> q;
		
		void add(int u,int v,int w)
		{
			e[++cnt].to=v;
			e[cnt].w=w;
			e[cnt].nxt=head[u];
			head[u]=cnt;
		}
		
		void add_edge(int u,int v,int w)
		{
			add(u,v,w);add(v,u,w);
		}
		
		bool bfs()
		{
			for(int i=0;i<=n;i++) dep[i]=-1,cur[i]=head[i];
			q.push(st);dep[st]=0;
			while(!q.empty())
			{
				int u=q.front();q.pop();
				for(int i=head[u];i;i=e[i].nxt)
				{
					int v=e[i].to;
					if(dep[v]==-1&&e[i].w)
					{
						dep[v]=dep[u]+1;
						q.push(v);
					}
				}
			}
			return dep[ed]!=-1;
		}
		
		int dfs(int u,int flow)
		{
			if(u==ed) return flow;
			int used=0,w;
			for(int &i=cur[u];i;i=e[i].nxt)//当前弧优化
			{
				int v=e[i].to;
				if(dep[v]==dep[u]+1&&e[i].w)
				{
					w=dfs(v,min(e[i].w,flow-used));
					e[i].w-=w;
					e[i^1].w+=w;
					used+=w;
					if(used==flow) return used;
				}
			}
			if(!used) dep[u]=-1;
			return used;
		}
		
		void init(int s,int t)
		{
			st=s;ed=t;
			for(int i=2;i<=cnt;i+=2)
			{
				e[i].w=(e[i].w+e[i^1].w)/2;
				e[i^1].w=e[i].w;
			}
		}
		
		int dinic(int s,int t)
		{
			init(s,t);
			int res=0,fl;
			while(bfs())
			{
				fl=dfs(st,inf);
				res+=fl;
			}
			return res;
		}
		
	}
	
	namespace mincut_tree
	{
		int cnt;
		int head[maxn],tmp1[maxn],tmp2[maxn],pos[maxn],ans[maxn][maxn];
		struct edge
		{
			int to,nxt,w;
		}e[maxm<<1];
		
		void add(int u,int v,int w)
		{
			e[++cnt].to=v;
			e[cnt].nxt=head[u];
			e[cnt].w=w;
			head[u]=cnt;
		}
		
		void add_edge(int u,int v,int w)
		{
			add(u,v,w);add(v,u,w);
		}
		
		void build(int l,int r)
		{
			if(l==r) return ;
			int cnt1=0,cnt2=0;
			add_edge(pos[l],pos[l+1],network::dinic(pos[l],pos[l+1]));
			for(int i=l;i<=r;i++) if(network::dep[pos[i]]!=-1) tmp1[++cnt1]=pos[i];else tmp2[++cnt2]=pos[i];
			for(int i=l;i<=l+cnt1-1;i++) pos[i]=tmp1[i-l+1];
       		for(int i=l+cnt1;i<=r;i++) pos[i]=tmp2[i-cnt1-l+1];
			build(l,l+cnt1-1);
			build(l+cnt1,r);
		}
		
		void dfs(int u,int ff,int id)
		{
			for(int i=head[u];i;i=e[i].nxt)
			{
				int v=e[i].to;
				if(v==ff) continue;
				ans[id][v]=min(ans[id][u],e[i].w);
				dfs(v,u,id);
			}
		}
		
		void work()
		{
			memset(ans,0x3f,sizeof(ans));
			for(int i=0;i<=n;i++) pos[i]=i;
			random_shuffle(pos,pos+n+1);
			build(0,n);
			for(int i=0;i<=n;i++) 
			{
				dfs(i,-1,i);
			}
		}
	}
	
	
	void work()
	{
		int a,b,c;
		n=read();m=read();
		for(int i=1;i<=m;i++)
		{
			a=read();b=read();c=read();
			network::add_edge(a,b,c);
		}
		mincut_tree::work();
		qt=read();
		while(qt--)
		{
			a=read();b=read();
			printf("%d\n",mincut_tree::ans[a][b]);
		}
	}

}

int main()
{
	zzc::work();
	return 0;
}

posted @ 2021-01-12 23:34  youth518  阅读(56)  评论(0编辑  收藏  举报