洛谷 P1967

题目链接:P1967 货车运输

题目大意

开局一张图装备全靠打

题目给了你一张图,然后呢, 让你找两点之间的路径,使路径上的最小值最大.(大概就是这个意思)

solution

思路

最小值最大? 二分? 复杂度不对啊

Floyd? 复杂度更不对了!

那我们怎么想呢? 贪心? 对, 就是贪心!

我们建一颗最大生成树,也就是最大瓶颈生成树(如果会生成树,这个地方就明白了为什么建最大生成树是对的), 树上两点间的路径的最小值一定是最大值

那建完树之后,我们怎么做呢?树剖?没必要,太浪费了,毕竟连修改都木有

我们简单的倍增一下就可以了,树上两点之间路径是唯一的,所以我们可以 \(LCA\) ,记录一下,树上倍增求最小值, 在处理 \(LCA\) 的时候顺便处理最小值即可

细节

数据非常的毒瘤啊, 有时候图还不是一张连通图

那我们怎么办? 其实很简单,我们在建树的时候,用并查集存过了, 所以我们在求 \(LCA\) 的时候求一下两点在没在同一个集合里就行了

code:

/**
 *    Author: Alieme
 *    Data: 2020.8.25
 *    Problem: Luogu P1967
 *    Time: O(nlogn)
 */
#include <cstdio>
#include <iostream>
#include <string>
#include <cstring>
#include <cmath>
#include <algorithm>

#define int long long
#define rr register

#define inf 1e9
#define MAXN 100010

using namespace std;

inline int read() {
	int s = 0, f = 0;
	char ch = getchar();
	while (!isdigit(ch)) f |= ch == '-', ch = getchar();
	while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar();
	return f ? -s : s;
}

void print(int x) {
	if (x < 0) putchar('-'), x = -x;
	if (x > 9) print(x / 10);
	putchar(x % 10 + 48);
}

struct Gragh {
	int u;
	int v;
	int w;
	Gragh() {}
	Gragh(int U, int V, int W) { u = U, v = V, w = W;}
	bool operator < (const Gragh &b) const{return w > b.w;} // 贪心
}a[MAXN];

struct Edge {
	int nxt;
	int to;
	int val;
	Edge() {}
	Edge(int Nxt, int To, int Val) {nxt = Nxt, to = To, val = Val;}
}e[MAXN];

int n, m, q, tot;

int fath[MAXN], head[MAXN], dep[MAXN];

int fa[MAXN][30], w[MAXN][30];

bool vis[MAXN];

inline void add(int from, int to, int val) {
	e[++tot] = Edge(head[from], to, val);
	head[from] = tot;
}

int find(int x) { // 并查集
	if (x != fath[x]) fath[x] = find(fath[x]);
	return fath[x];
}

inline void Union(int x, int y) { fath[find(x)] = find(y);}

inline void klus() {  // klus建最大生成树
	sort(a + 1, a + 1 + m);
	for (rr int i = 1; i <= n; i++) fath[i] = i;
	for (rr int i = 1; i <= m; i++) 
		if (find(a[i].u) != find(a[i].v)) {
			Union(a[i].u, a[i].v);
			add(a[i].u, a[i].v, a[i].w);
			add(a[i].v, a[i].u, a[i].w);
		}
}

void dfs(int x) { // LCA的dfs
	vis[x] = 1;
	for (rr int i = head[x]; i; i = e[i].nxt) {
		int to = e[i].to;
		if (vis[to]) continue;
		dep[to] = dep[x] + 1;
		fa[to][0] = x;
		w[to][0] = e[i].val;
		dfs(to);
	}
}

inline int LCA(int u, int v) { // 树上倍增找最小值
	if (find(u) != find(v)) return -1; // 两点不联通
	int ans = inf;
	if (dep[u] > dep[v]) swap(u, v);
	for (rr int i = 20; i >= 0; i--) 
		if (dep[fa[v][i]] >= dep[u]) {
			ans = min(ans, w[v][i]);
			v = fa[v][i];
		}
	if (u == v) return ans;
	for (rr int i = 20; i >= 0; i--) {
		if (fa[u][i] != fa[v][i]) {
			ans = min(ans, min(w[u][i], w[v][i]));
			u = fa[u][i];
			v = fa[v][i];
		}
	}
	ans = min(ans, min(w[u][0], w[v][0]));
	return ans;
}

signed main() {
	n = read();
	m = read();
	for (rr int i = 1; i <= m; i++) {
		int u = read();
		int v = read();
		int w = read();
		a[i] = Gragh(u, v, w);
	}
	klus();	
//  LCA初始化
	for (rr int i = 1; i <= n; i++) 
		if (!vis[i]) {
			dep[i] = 1;
			dfs(i);
			fa[i][0] = i;
			w[i][0] = inf;
		}
	for (rr int i = 1; i <= 20; i++) 
		for (rr int j = 1; j <= n; j++) {
			fa[j][i] = fa[fa[j][i - 1]][i - 1];
			w[j][i] = min(w[j][i - 1], w[fa[j][i - 1]][i - 1]);
		}

	q = read();
	while (q--) {
		int x = read();
		int y = read();
		print(LCA(x, y));
		puts("");
	}
}
posted @ 2020-08-25 15:41  Aliemo  阅读(101)  评论(0编辑  收藏  举报