Codeforces Round 888 (Div. 3) G. Vlad and the Mountains 并查集

题目链接

题目大意

        n ( 2<=n<=2*10^5 ) 座山, i 山有高 h [ i ] ( 1<= h [ i ]<= 10^9)。有 m ( 1<=m<=min( n*(n-1)/2,2*10^5 ) 条路,如果 i 山和 j 山之间有路,可以花费 h [ j ] − h [ i ] 个单位的能量从 i 山移动到 j 山。如果他的能量在转换过程中降至零以下,他将无法从 i 山移动到 j 山。注意 h [ j ] − h [ i ]可以是负值,然后能量将增加。q (1<=q<=2*10^5 )次询问,每次问:最初拥有 e 单位能量的情况下,是否有可能构建一条从 a 山开始到 b 山结束的路线。

思路

        从a出发,初始能量为e,最多只能走到高为 h [ a ]+e 的山。则q次询问就转为了,只经过 <=h[ a ]+e 的点能否到达 b山。

        将q次询问按 h [ a ]+e 从小到大排序,用并查集维护图的连通性,每次加入<= h [ a ]+e 的点加完之后判是否连通即可。

code

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 100;
int h[N], id[N],fa[N];
int n, m;
vector<int>p[N];

struct node {
	int maxn, a, b, wz, ans;
	bool operator<(const node& x)const {
		return maxn < x.maxn;
	}
}a[N];//存入q次询问的信息

bool cmp(int x, int y) {
	return h[x] < h[y];
}

bool cmp1(node x, node y) {
	return x.wz < y.wz;
}

signed find(int x) {
	return fa[x] == x ? x : fa[x] = find(fa[x]);
}

void merge(int a, int b) {
	a = find(a), b = find(b);
	fa[a] = b;
}

void solve() {
	cin >> n >> m;
	for (int i = 1; i <= n; ++i) {
		fa[i] = i, id[i] = i, p[i].clear();
	}
	for (int i = 1; i <= n; ++i) cin >> h[i];
	for (int i = 1; i <= m; ++i) {
		int u, v;
		cin >> u >> v;
		p[u].push_back(v);
		p[v].push_back(u);
	}
	int q;
	cin >> q;
	for (int i = 1; i <= q; ++i) {
		int u, v, e;
		cin >> u >> v >> e;
		a[i] = { h[u] + e,u,v,i,0 };
	}
	sort(id + 1, id + n + 1, cmp);
	sort(a + 1, a + q + 1);
	int j = 1;
	for (int i = 1; i <= q; ++i) {
		while (j <= n && h[id[j]] <= a[i].maxn) {
			for (auto k : p[id[j]]) {
				if (h[k] <= a[i].maxn)//当改边的两个点都<=h[a]+e时加入连通块中
					merge(id[j], k);
			}
			j++;
		}
		if (find(a[i].a) == find(a[i].b)) a[i].ans = 1;
	}
	sort(a + 1, a + q + 1, cmp1);//恢复为询问的顺序
	for (int i = 1; i <= q; ++i) {
		if (a[i].ans) cout << "YES\n";
		else cout << "NO\n";
	}
	cout << "\n";
}

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	int t = 1;
	cin >> t;
	while (t--) solve();
	return 0;
}

        

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值