题目链接: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("");
}
}