BZOJ 3732: Network Kruskal重构树 || 最小生成树+LCA

版权声明:https://blog.csdn.net/huashuimu2003 https://blog.csdn.net/huashuimu2003/article/details/88923631

title

BZOJ 3723
Description

给你N个点的无向图 (1 <= N <= 15,000),记为:1…N。
图中有M条边 (1 <= M <= 30,000) ,第j条边的长度为: d_j ( 1 < = d_j < = 1,000,000,000).
现在有 K个询问 (1 < = K < = 20,000)。
每个询问的格式是:A B,表示询问从A点走到B点的所有路径中,最长的边最小值是多少?

Input

第一行: N, M, K。
第2…M+1行: 三个正整数:X, Y, and D (1 <= X <=N; 1 <= Y <= N). 表示X与Y之间有一条长度为D的边。
第M+2…M+K+1行: 每行两个整数A B,表示询问从A点走到B点的所有路径中,最长的边最小值是多少?

Output

对每个询问,输出最长的边最小值是多少。

Sample Input

6 6 8
1 2 5
2 3 4
3 4 3
1 4 8
2 5 7
4 6 2
1 2
1 3
1 4
2 3
2 4
5 1
6 2
6 1

Sample Output

5
5
5
4
4
7
4
5

HINT

1 <= N <= 15,000
1 <= M <= 30,000
1 <= d_j <= 1,000,000,000
1 <= K <= 15,000

analysis

其实这道题的题意简化一下,就是在一个图中询问任意两点之间的路径,使得路径上的最大边最小
感觉这个题目似曾相识,没错就是货车运输,只要先建立一棵最小生成树,在树上跑 l c a lca 就可以了。

但是今天我们不要用这么low的算法(不错,就是没事找事),我们就引进一种新的数据结构 k r u s k a l kruskal重构树

什么是 k r u s k a l kruskal重构树 呢:
k r u s k a l kruskal重构树 可以拿来处理一些 最小生成树的边权最值问题 ,这里我们 K r u s k a l Kruskal 连边时并不直接连边,而是新建一个节点 x x ,将两个点所在子树都连到 x x 的儿子上。

这样生成的树有一些十分优美的性质:

  • 1.二叉树。
  • 2.原树与新树两点间路径上边权(点权)的最大值相等 。
  • 3.子节点的边权小于等于父亲节点(大根堆) 。
  • 4.原树中两点之间路径上边权的最大值等于新树上两点的 L C A LCA 的点权。
    在这里插入图片描述圆点是原图中的点,方点是原图中的边。

那怎么构建 k r u s k a l kruskal重构树 呢:
其实就像构建最小生成树一样,只不过并不直接连边,而是新建一个节点 x x ,将两个点所在子树都连到 x x 的儿子上,然后处理询问只要找一下 l c a lca 就可以了。

code

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
template<typename T>inline void read(T &x)
{
	x=0;
	T f=1, ch=getchar();
	while (!isdigit(ch) && ch^'-') ch=getchar();
	if (ch=='-') f=-1, ch=getchar();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	x*=f;
}
struct Edge
{
	int x,y,z;
	inline bool operator < (const Edge &a) const
	{
		return z<a.z;
	}
}e[maxn];
int len=0;
inline void add(int x,int y,int z)
{
	e[++len]=(Edge){x,y,z};
}
int n,m,k,cnt,fa[maxn];
inline int get(int x)
{
	return fa[x]==x?x:fa[x]=get(fa[x]);
}
int son[maxn][2],val[maxn];
int f[maxn][21],d[maxn];
inline void Kruskal()
{
	sort(e+1,e+len+1);
	for (int i=1; i<=m; ++i)
	{
		int x=get(e[i].x),y=get(e[i].y);
		if (x==y) continue;
		son[++cnt][0]=x,son[cnt][1]=y;
        fa[fa[x]]=fa[fa[y]]=f[fa[x]][0]=f[fa[y]][0]=cnt;
        val[cnt]=e[i].z;
	}
}
inline void dfs(int x)
{
	if (!son[x][0] && !son[x][1]) return ;
    d[son[x][0]]=d[son[x][1]]=d[x]+1;
    dfs(son[x][0]);
	dfs(son[x][1]);
}
inline int lca(int x,int y)
{
	if (d[x]>d[y]) swap(x,y);
	for (int i=20; i>=0; --i)
		if (d[f[y][i]]>=d[x])
			y=f[y][i];
	if (x==y) return x;
	for (int i=20; i>=0; --i)
		if (f[x][i]^f[y][i])
			x=f[x][i],y=f[y][i];
	return f[x][0];
}
int main()
{
	read(n);read(m);read(k);cnt=n;
	for (int i=1; i<=(n<<1); ++i)
		fa[i]=i;
	for (int i=1; i<=m; ++i)
	{
		int x,y,z;
		read(x);read(y);read(z);
		add(x,y,z);
	}
	Kruskal();
	d[cnt]=1;
	dfs(cnt);
	for (int i=1; i<=20; ++i)
		for (int j=1; j<=(n<<1); ++j)
			f[j][i]=f[f[j][i-1]][i-1];
	while (k--)
	{
		int x,y;
		read(x);read(y);
		printf("%d\n",val[lca(x,y)]);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/huashuimu2003/article/details/88923631