雪之国度

雪之国度 ⁡ \operatorname{雪之国度}

题目链接: 51nod 1743 ⁡ \operatorname{51nod\ 1743} 51nod 1743

题目

雪之国度有 N N N 座城市,依次编号为 1 1 1 N N N ,又有 M M M 条道路连接了其中的城市,每一条道路都连接了不同的 2 2 2 个城市,任何两座不同的城市之间可能不止一条道路。
雪之女王赋予了每一座城市不同的能量,其中第i座城市被赋予的能量为 W i W_i Wi
如果城市 u u u v v v 之间有一条道路,那么只要此刻雪之女王的能量不小于 ∣ W u − W v ∣ |W_u-W_v| WuWv ,这条道路就是安全的。
如果城市 u u u v v v 之间存在两条没有重复道路的安全路径(其中每一段道路都是安全的),则认为这两座城市之间有着良好的贸易关系。
最近,雪之女王因为情感问题,她的能量产生巨大的波动。为了维持雪之国度的经济贸易,她希望你能帮忙对 Q Q Q 对城市进行调查。
对于第 j j j 对城市 u j u_j uj v j v_j vj ,她希望知道在保证这两座城市之间有着良好贸易关系的前提之下,自己最少需要保持多少的能量。

输入

每一组数据第一行有 3 3 3 个整数,依次为 N , M , Q N,M,Q N,M,Q ,表示城市个数,道路个数,和所需要进行的调查次数。
之后一行,有 N N N 个整数,依次为每一个城市被赋予的能量 W i W_i Wi
之后 M M M 行,每一行有 2 2 2 个整数,表示对应编号的两个城市之间有一条道路。
之后 Q Q Q 行,每一行有 2 2 2 个整数,表示一组调查的城市目标。
对于 100 % 100% 100 的数据来说, 3 < = N < = 100000 , 3 < = M < = 500000 , 1 < = Q < = 100000 3<=N<=100000, 3<=M<=500000, 1<=Q<=100000 3<=N<=100000,3<=M<=500000,1<=Q<=100000 ,每一座城市的能量 W i W_i Wi 满足 0 < = W i < = 200000. 0<=W_i<=200000. 0<=Wi<=200000.

输出

输出一共有 Q Q Q 行,依次对应 Q Q Q 次调查的结果。
其中第 j j j 行给出了第 j j j 次调查的结果,即雪之女王需要保持的最少能量值。如果永远也无法做到,输出 “infinitely” 。

样例输入

7 8 4
3 2 4 1 3 5 9
1 2
1 3
2 4
2 5
3 6
6 7
4 6
5 6
4 5
4 6
5 6
2 7

样例输出

4
4
2
infinitely

思路

这道题要用最小生成树和 LCA 来做。

我们可以先建一个最小生成树,然后再慢慢的按权值从小到大加上之前没有的边。
那我们就可以发现,每次加边会形成一个环,而在这个环中的所有点两两之间都会有两条道路可以走。而要保持这两条路可以走所要的魔力值,就是最新加上的那一条边。

那我们就用并查集维护,然后用 LCA 就可以求出两点之间的费用了。

代码

#include<cstdio>
#include<algorithm>

using namespace std;

struct road {
    
    
	int x, y, z;
}b[500001];
struct node {
    
    
	int to, nxt;
}e[1000001];
int n, m, q, a[100001], nownum, fa[100001], le[100001], KK;
int f[21][100001], dep[100001], ff[21][100001];
bool use[500001];

bool cmp(road x, road y) {
    
    
	return x.z < y.z;
}

int find(int now) {
    
    //并查集操作
	if (now == fa[now]) return now;
	return fa[now] = find(fa[now]);
}

void add(int x, int y) {
    
    //建图
	e[++KK] = (node){
    
    y, le[x]}; le[x] = KK;
	e[++KK] = (node){
    
    x, le[y]}; le[y] = KK;
}

void dfs(int now, int father) {
    
    //LCA要用的遍历跑图
	dep[now] = dep[father] + 1;
	f[0][now] = father;
	for (int i = le[now]; i; i = e[i].nxt)
		if (e[i].to != father)
			dfs(e[i].to, now);
}

int lca(int x, int y) {
    
    //lca
	int re = 0;
	if (dep[x] < dep[y]) swap(x, y);
	for (int i = 20; i >= 0; i--)
		if (dep[f[i][x]] >= dep[y]) {
    
    
			re = max(re, ff[i][x]);//记录答案
			x = f[i][x];
		}
	if (x == y) return re;
	for (int i = 20; i >= 0; i--)
		if (f[i][x] != f[i][y]) {
    
    
			re = max(re, max(ff[i][x], ff[i][y]));//记录答案
			x = f[i][x];
			y = f[i][y];
		}
	return max(re, max(ff[0][x], ff[0][y]));
}

int main() {
    
    
	scanf("%d %d %d", &n, &m, &q);//读入
	for (int i = 1; i <= n; i++) {
    
    
		scanf("%d", &a[i]);
		fa[i] = i;//初始化
	}
	for (int i = 1; i <= m; i++) {
    
    
		scanf("%d %d", &b[i].x, &b[i].y);//读入
		b[i].z = abs(a[b[i].x] - a[b[i].y]);//求出距离
	}
	
	sort(b + 1, b + m + 1, cmp);//最小生成树
	for (int i = 1; i <= m; i++) {
    
    
		int x = find(b[i].x), y = find(b[i].y);
		if (x == y) continue;
		
		fa[x] = y;
		use[i] = 1;
		add(b[i].x, b[i].y);
		
		nownum++;
		if (nownum == n - 1) break;
	}
	
	dfs(1, 0);//遍历以及预处理
	for (int i = 1; i <= 20; i++)
		for (int j = 1; j <= n; j++)
			f[i][j] = f[i - 1][f[i - 1][j]];
	
	nownum = 0;//对于没有在最小生成树上的边做处理
	for (int i = 1; i <= n; i++) fa[i] = i;
	for (int i = 1; i <= m; i++)
		if (!use[i]) {
    
    
			int x = find(b[i].x), y = find(b[i].y);
			if (x == y) continue;
			
			while (x != y) {
    
    //更新权值
				if (dep[x] < dep[y]) swap(x, y);
				ff[0][x] = max(ff[0][x], b[i].z);
				fa[x] = f[0][x];
				x = find(x);
			}
			
			nownum++;
			if (nownum == n - 1) break;
		}
	
	for (int i = 1; i <= 20; i++)//预处理距离
		for (int j = 1; j <= n; j++)
			ff[i][j] = max(ff[i - 1][j], ff[i - 1][f[i - 1][j]]);
	
	for (int i = 1; i <= q; i++) {
    
    
		int x, y, xx, yy;
		scanf("%d %d", &x, &y);//读入
		xx = find(x), yy = find(y);
		if (xx != yy) printf("infinitely\n");//输出
			else printf("%d\n", lca(x, y));
	}
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43346722/article/details/108244900
今日推荐