\(\color{#0066ff}{ 题目描述 }\)

sideman 做好了回到 \(\text{Gliese}\) 星球的硬件准备,但是 \(\text{sideman}\) 的导航系统还没有完全设计好。为了方便起见,我们可以认为宇宙是一张有$ N$ 个顶点和 \(M\) 条边的带权无向图,顶点表示各个星系,两个星系之间有边就表示两个星系之间可以直航,而边权则是航行的危险程度。

\(\text{sideman}\) 现在想把危险程度降到最小,具体地来说,就是对于若干个询问 \((A, B)\),\(\text{sideman}\) 想知道从顶点 \(A\)航行到顶点 \(B\) 所经过的最危险的边的危险程度值最小可能是多少。作为 \(\text{sideman}\) 的同学,你们要帮助 \(\text{sideman}\)返回家园,兼享受安全美妙的宇宙航行。所以这个任务就交给你了。

\(\color{#0066ff}{输入格式}\)

第一行包含两个正整数 \(N\) 和 \(M\),表示点数和边数。

之后 \(M\) 行,每行三个整数 \(A\),\(B\) 和 \(L\),表示顶点 \(A\) 和 \(B\) 之间有一条边长为 \(L\) 的边。顶点从 \(1\) 开始标号。

下面一行包含一个正整数 \(Q\),表示询问的数目。

之后 \(Q\) 行,每行两个整数 \(A\) 和 BB,表示询问 \(A\) 和 \(B\) 之间最危险的边危险程度的可能最小值。

\(\color{#0066ff}{输出格式}\)

对于每个询问, 在单独的一行内输出结果。如果两个顶点之间不可达, 输出 \(\text{impossible}\)。

\(\color{#0066ff}{输入样例}\)

4 5
1 2 5
1 3 2
2 3 11
2 4 6
3 4 4
3
2 3
1 4
1 2

\(\color{#0066ff}{输出样例}\)

5
4
5

\(\color{#0066ff}{数据范围与提示}\)

对于 \(40\%\) 的数据,满足 \(N \leq 1000, M \leq 3000, Q \leq 1000\)。

对于 \(80\%\) 的数据,满足 \(N \leq 10000, M \leq 10^5, Q \leq 1000\)。

对于 $100%$100% 的数据,满足 \(N \leq 10^5, M \leq 3 \times 10^5, Q \leq 10^5, L \leq 10^9\)。数据不保证没有重边和自环。

\(\color{#0066ff}{ 题解 }\)

一个无向图,看到最大边最小,我去,最小生成树。。。

然后神奇的发现,题目不保证没有重边自环的问题就解决了

于是我们就得到了一个森林

每次询问两个点,如果不在一棵树中(通过最小生成树的ufs判断)输出无解

否则输出其路径上最大边边权,显然倍增搞一下就行了

#include<bits/stdc++.h>
#define LL long long
LL in() {
	char ch; LL x = 0, f = 1;
	while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
	for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
	return x * f;
}
int n, m;
const int maxn = 1e5 + 10;
struct node {
	int to, dis;
	node *nxt;
	node(int to = 0, int dis = 0, node *nxt = NULL): to(to), dis(dis), nxt(nxt) {}
	void *operator new(size_t) {
		static node *S = NULL, *T = NULL;
		return (S == T) && (T = (S = new node[1024]) + 1024), S++;
	}
}*head[maxn];
bool vis[maxn];
int dep[maxn];
struct E {
	int x, y, dis;
	friend bool operator < (const E &a, const E &b) {
		return a.dis < b.dis;
	}
}e[maxn << 2];
int f[maxn][25], max[maxn][25];
int b[maxn];
void add(int from, int to, int dis) {
	head[from] = new node(to, dis, head[from]);
}
int findset(int x) { return x == b[x]? b[x] : b[x] = findset(b[x]); }
void Kruskal() {
	for(int i = 1; i <= n; i++) b[i] = i;
	for(int i = 1; i <= m; i++) e[i].x = in(), e[i].y = in(), e[i].dis = in();
	std::sort(e + 1, e + m + 1);
	for(int i = 1; i <= m; i++) {
		int xx = findset(e[i].x);
		int yy = findset(e[i].y);
		if(xx != yy) {
			b[xx] = yy;
			add(e[i].x, e[i].y, e[i].dis);
			add(e[i].y, e[i].x, e[i].dis);
		}
	}
}
void dfs(int x, int fa) {
	vis[x] = true;
	dep[x] = dep[fa] + 1;
	f[x][0] = fa;
	for(node *i = head[x]; i; i = i->nxt) {
		if(i->to == fa) continue;
		max[i->to][0] = i->dis;
		dfs(i->to, x);
	}
}
void beizeng() {
	for(int i = 1; i <= n; i++) if(!vis[i]) dfs(i, 0);
	for(int j = 1; j <= 24; j++)
		for(int i = 1; i <= n; i++) {
			f[i][j] = f[f[i][j - 1]][j - 1];
			max[i][j] = std::max(max[i][j - 1], max[f[i][j - 1]][j - 1]);
		}
}
int LCA(int x, int y) {
	int dis = 0;
	if(dep[x] < dep[y]) std::swap(x, y);
	for(int i = 24; i >= 0; i--) 
		if(dep[f[x][i]] >= dep[y]) 
			dis = std::max(dis, max[x][i]), x = f[x][i];
	if(x == y) return dis;
	for(int i = 24; i >= 0; i--) 
		if(f[x][i] != f[y][i]) {
			dis = std::max(dis, std::max(max[x][i], max[y][i]));
			x = f[x][i], y = f[y][i];
		}
	return std::max(dis, std::max(max[x][0], max[y][0]));
}

void query() {
	int x, y;
	for(int T = in(); T --> 0;) {
		x = in(), y = in();
		if(findset(x) != findset(y)) printf("impossible\n");
		else printf("%d\n", LCA(x, y));
	}
}

int main() {
	n = in(), m = in();
	Kruskal();
	beizeng();
	query();
	return 0;
}
----olinr