1. 程式人生 > 實用技巧 >洛谷 P1967

洛谷 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("");
	}
}