BZOJ 3732 Network【kruskal重构树、树上倍增、树链剖分】

无向图, N N N个节点,表示询问从 A A A点走到 B B B点的所有路径中,最的边最值是多少?

K r u s k a l Kruskal Kruskal​​​ 重构树 or 树链剖分 or 树上倍增

传送门

分析

对于此题分析:
在所有路径中取最长的那一条边,使得边最小
假设找到了这条路径
那么这条路径里面的其它边必然小于等于这条边,且这时候 A A A B B B 是联通的,也就是说,其它能够联通 A A A B B B路径中,一定会存在大于等于这个值的边。
假如我们在图中删除所有大于等于这个值的边, A A A B B B 就一定不连通了。因此,我们需要找到恰好能够联通 A A A B B B 的边长度就行了。
这不就是最小生成树吗?
从最小的边开始加,一旦发现 A A A B B B 联通了,联通 A A A B B B的那条路径里面的最大边就是所求!

我们能够在执行 K r u s k a l Kruskal Kruskal 的过程中,构造一棵最小生成树。用树链剖分,或者树上倍增维护链上的边最大值

关于 K r u s k a l Kruskal Kruskal 重构树,同样在执行 K r u s k a l Kruskal Kruskal的时候,如果要将两个点用并查集合并的时候,建立一个新节点,此新节点分别向这两个点( f i n d find find后的值,也就是连通块的祖先)连一条有向边,新节点的点权为新加入边的边权,当算法执行完后,就完成了 K r u s k a l Kruskal Kruskal重构树的构建
重构树的性质(最 /大 生成树的情况下)

  • 某重构树中的节点,的子树都能在通过 /大 于等于此节点点权的情况下到达
  • L C A ( u , v ) LCA(u,v) LCA(u,v)的权值为 u u u v v v所有路径中最 /小 边最 /长 的值
  • 重构树为一个 /小 顶堆

此题的情况下,构建一颗重构树,直接在重构树中找LCA即可

t i p s : tips: tips因为所有的原图点都是通过新建点相连的,所以任意两个原图点 l c a lca lca必然是新建点,不同担心找错了 l c a lca lca,从而得到错误点权

代码

Kruskal重构树

//bz3732 
/*
  @Author: YooQ
*/
#include <bits/stdc++.h>
using namespace std;
#define sc scanf
#define pr printf
#define ll long long
#define int long long
#define FILE_OUT freopen("out", "w", stdout);
#define FILE_IN freopen("in", "r", stdin);
#define debug(x) cout << #x << ": " << x << "\n";
#define AC 0
#define WA 1
#define INF 0x3f3f3f3f
const ll MAX_N = 1e6+5;
const ll MOD = 1e9+7;
int N, M, K;


int head[MAX_N];
int tot = 0;
struct Edge{
	int to, nxt;
}edge[MAX_N];

void addEdge(int u, int v) {
	edge[tot].nxt = head[u];
	edge[tot].to = v;
	head[u] = tot++;
}

struct Node {
	int u, v, w;
	bool operator < (const Node& B) const {
		return w < B.w;
	}
}arr[MAX_N];

int parent[MAX_N];

int find(int x) {
	return x==parent[x]?x:parent[x]=find(parent[x]);
}

int father[MAX_N][21];
int dep[MAX_N];

void dfs(int u, int from, int d) {
	father[u][0] = from;
	dep[u] = d;
	for (int i = 1; i <= 20; ++i) {
		father[u][i] = father[father[u][i-1]][i-1];
	}
	int v;
	for (int i = head[u];~i;i=edge[i].nxt) {
		dfs(v=edge[i].to, u, d+1);
	}
}

int LCA(int x, int y) {
	if (dep[x] > dep[y]) swap(x, y);
	for (int i = 20; i+1; --i) {
		if (dep[father[y][i]] >= dep[x]) {
			y = father[y][i];
		}
	}
	if (x == y) return x;
	for (int i = 20; i+1; --i) {
		if (father[x][i] != father[y][i]) {
			x = father[x][i];
			y = father[y][i];
		}
	}
	return father[x][0];
}

int brr[MAX_N];
int indx = 0;

void init() {
	memset(head, -1, sizeof head);
	tot = 0;
}

void solve(){
	sc("%lld%lld%lld", &N, &M, &K);
	init();
	for (int i = 1; i <= 2*N; ++i) {
		parent[i] = i;
	}
	for (int i = 1; i <= M; ++i) {
		sc("%lld%lld%lld", &arr[i].u, &arr[i].v, &arr[i].w);
	}
	sort(arr+1, arr+1+M);
	int x, y;
	indx = N;
	for (int i = 1; i <= M; ++i) {
		x = find(arr[i].u);
		y = find(arr[i].v);
		if (x != y) {
			parent[x] = parent[y] = ++indx;
			brr[indx] = arr[i].w;
			addEdge(indx, x);
			addEdge(indx, y);
		}
	}
	dfs(indx, 0, 1);
	
	for (int i = 1; i <= K; ++i) {
		sc("%lld%lld", &x, &y);
		pr("%lld\n", brr[LCA(x, y)]);
	}
}

signed main()
{
	#ifndef ONLINE_JUDGE
	//FILE_IN
	FILE_OUT
	#endif
	int T = 1;//cin >> T;
	while (T--) solve();

	return AC;
}
 

树上倍增

树链剖分同样思路,维护区间最值

//bz3732 
/*
  @Author: YooQ
*/
#include <bits/stdc++.h>
using namespace std;
#define sc scanf
#define pr printf
#define ll long long
#define int long long
#define FILE_OUT freopen("out", "w", stdout);
#define FILE_IN freopen("in", "r", stdin);
#define debug(x) cout << #x << ": " << x << "\n";
#define AC 0
#define WA 1
#define INF 0x3f3f3f3f
const ll MAX_N = 1e6+5;
const ll MOD = 1e9+7;
int N, M, K;


int head[MAX_N];
int tot = 0;
struct Edge{
	int to, nxt, w;
}edge[MAX_N];

void addEdge(int u, int v, int w) {
	edge[tot].nxt = head[u];
	edge[tot].to = v;
	edge[tot].w = w;
	head[u] = tot++;
	edge[tot].nxt = head[v];
	edge[tot].to = u;
	edge[tot].w = w;
	head[v] = tot++;
}

struct Node {
	int u, v, w;
	bool operator < (const Node& B) const {
		return w < B.w;
	}
}arr[MAX_N];

int parent[MAX_N];

int find(int x) {
	return x==parent[x]?x:parent[x]=find(parent[x]);
}

int father[MAX_N][21];
int maxx[MAX_N][21];
int dep[MAX_N];

void dfs(int u, int from, int d) {
	father[u][0] = from;
	dep[u] = d;
	for (int i = 1; i <= 20; ++i) {
		father[u][i] = father[father[u][i-1]][i-1];
		maxx[u][i] = max(maxx[u][i-1], maxx[father[u][i-1]][i-1]);
	}
	int v;
	for (int i = head[u];~i;i=edge[i].nxt) {
		if ((v=edge[i].to) == from) continue;
		maxx[v][0] = edge[i].w;
		dfs(v, u, d+1);
	}
}

int query(int x, int y) {
	if (dep[x] > dep[y]) swap(x, y);
	int ans = 0;
	for (int i = 20; i+1; --i) {
		if (dep[father[y][i]] >= dep[x]) {
			ans = max(ans, maxx[y][i]);
			y = father[y][i];
		}
	}
	for (int i = 20; i+1; --i) {
		if (father[x][i] != father[y][i]) {
			ans = max(ans, max(maxx[x][i], maxx[y][i]));
			x = father[x][i];
			y = father[y][i];
		}
	}
	if (x != y) {
		ans = max(ans, max(maxx[x][0], maxx[y][0]));
	}
	return ans;
}

void init() {
	memset(head, -1, sizeof head);
	tot = 0;
}

void solve(){
	sc("%lld%lld%lld", &N, &M, &K);
	init();
	for (int i = 1; i <= N; ++i) {
		parent[i] = i;
	}
	for (int i = 1; i <= M; ++i) {
		sc("%lld%lld%lld", &arr[i].u, &arr[i].v, &arr[i].w);
	}
	sort(arr+1, arr+1+M);
	int x, y;
	for (int i = 1; i <= M; ++i) {
		x = find(arr[i].u);
		y = find(arr[i].v);
		if (x != y) {
			parent[x] = y;
			addEdge(arr[i].u, arr[i].v, arr[i].w);
		}
	}
	dfs(1, 0, 1);
	
	for (int i = 1; i <= K; ++i) {
		sc("%lld%lld", &x, &y);
		pr("%lld\n", query(x, y));
	}
}

signed main()
{
	#ifndef ONLINE_JUDGE
	//FILE_IN
	FILE_OUT
	#endif
	int T = 1;//cin >> T;
	while (T--) solve();

	return AC;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hexrt

客官,请不要给我小费!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值