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

Problem Description

给出 n n n 座山,一些山之间有公路相连,第 i i i 座山高为 h i h_i hi

如果 i i i 山和 j j j 山之间有条路,那么 V l a d Vlad Vlad 可以花费 h j − h i h_j-h_i hjhi 的能量从 i i i 山转移到 j j j 山,如果转移过程中能量降到 0 0 0 以下,则无法转移。注意 h j − h i h_j-h_i hjhi 可以是负值, 对应为能量回复。

现在请你回答如果 V l a d Vlad Vlad 在能量初始为 e e e 的情况下,是否有可能从 a a a 山走到 b b b 山。

Input

第一行输入一个整数 t t t ,表示测试组数。

对于每组测试,

第一行输入 n , m ( 2 ≤ n ≤ 2 × 1 0 5 , 1 ≤ m ≤ m i n ( n ⋅ ( n − 1 ) 2 , 2 × 1 0 5 ) ) n,m(2 \le n \le 2 \times 10^5,1 \le m \le min(\frac{n·(n-1)}{2},2 \times 10^5)) n,m(2n2×105,1mmin(2n(n1),2×105)) ,表示山和路的个数。

第二行输入 n n n 整数 h 1 , h 2 , h 3 , … , h n ( 1 ≤ h i ≤ 1 0 9 ) h_1,h_2,h_3,…,h_n(1 \le h_i \le 10^9) h1,h2,h3,,hn(1hi109) ,表示每座山的高度。

接下来 m m m 行输入两个整数 u , v ( 1 ≤ u , v ≤ n , u ≠ v ) u,v(1 \le u,v \le n,u \not= v) u,v(1u,vn,u=v) ,表示 u u u 山和 v v v 山之间存在一条道路,不存在道路出现两次。

接下来一行输入一个整数 q ( 1 ≤ q ≤ 2 × 1 0 5 ) q(1 \le q \le 2 \times 10^5) q(1q2×105) ,表示查询次数。

接下来 q q q 行输入三个整数 a , b , e ( 1 ≤ a , b ≤ n , 0 ≤ e ≤ 1 0 9 ) a,b,e(1 \le a,b \le n,0 \le e \le 10^9) a,b,e(1a,bn,0e109) ,表示起点,终点和初始能量。

Output

对于每次询问,输出 “ Y e s Yes Yes” 或 “ N o No No” ,表示是否可达。

Solution

首先 a a a b b b 如果连通,那么经过的山其实只有所有路径最高的山中的最小高度才是关键限制,只要 h m a x − h a ≤ e h_{max}-h_a \le e hmaxhae 就能可达,也就是说需要 h m a x ≤ h a + e h_{max} \le h_a+e hmaxha+e 满足,那么这个表意也等同于初始山高为 h a + e h_a+e ha+e ,是否能找到一条路径,使路径上的所有山高都低于 h a + e h_a+e ha+e ,现在我们便需考虑如何快速求出 h m a x h_{max} hmax

考虑到 h m a x = m a x ( h a , h c , h d … , h e , h b ) = m a x ( m a x ( h a , h c ) , m a x ( h c , h d ) , … , m a x ( h e , h b ) ) h_{max}=max(h_a,h_c,h_d…,h_e,h_b)=max(max(h_a,h_c),max(h_c,h_d),…,max(h_e,h_b)) hmax=max(ha,hc,hd,he,hb)=max(max(ha,hc),max(hc,hd),,max(he,hb)),我们可以将从 i i i j j j 的边权设为 m a x ( h i , h j ) max(h_i,h_j) max(hi,hj) ,那么就转化成了 a a a b b b 所有路径最大边权的最小值。

又由于

原图中两点之间的所有简单路径上最大边权的最小值 = = = 最小生成树两点之间简单路径上的最大值。

我们可以去构建原图的最小生成树,然后求 a a a b b b 上的最大边权,对于这步查询,我们可以采用 L C A LCA LCA 等方式去以 O ( q l o g n ) O(qlogn) O(qlogn) 的时间复杂度便能通过此题,此处介绍另一种离线做法。

考虑到 K r u s k a l Kruskal Kruskal 最小生成树的做法,每次将剩余边中的最小边添加进图中,能否使两个不连通的块连通。

而每次添加了这样的最小边之后,图中的所有边权 ≤ \le 最小边,此时我们发现,只要将 w i ≤ h a + e w_i \le h_a+e wiha+e 的所有边加进来后,判断 a a a b b b 是否连通即可。

那么我们现在的做法就变成了先将所有查询存下来,然后以 h a + e h_a+e ha+e 为关键值排序,然后从小到大枚举每个查询,将 w i ≤ h a + e w_i \le h_a+e wiha+e 的边添加到图中, a , b a,b a,b 是否连通就变成了在并查集中 a , b a,b a,b 是否在同一连通块,最终复杂度为 O ( m l o g m + q l o g q ) O(mlogm+qlogq) O(mlogm+qlogq)

Code

#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
struct DSU {
	vector<int>p;
	DSU(int n): p(n) {iota(p.begin(), p.end(), 0);}
	int leader(int x) {
		while (x != p[x])x = p[x] = p[p[x]];
		return x;
	}
	bool merge(int a, int b) {
		a = leader(a), b = leader(b);
		if (a == b)return false;
		p[b] = a;
		return true;
	}
	bool same(int x, int y) {return leader(x) == leader(y);}
};
void solve() {
	int n, m;
	cin >> n >> m;
	vector<int>h(n + 1);
	for (int i = 1; i <= n; i++)cin >> h[i];
	vector<array<int, 3>>edge(m);
	for (int i = 1; i <= m; i++) {
		int a, b;
		cin >> a >> b;
		edge[i - 1] = {max(h[a], h[b]), a, b};
	}
	sort(edge.begin(), edge.end());
	int q;
	cin >> q;
	vector<int>ans(q);
	vector<array<int, 4>>query(q);
	for (int i = 0; i < q; i++) {
		int a, b, e;
		cin >> a >> b >> e;
		query[i] = {h[a] + e, a, b, i};
	}
	int j = 0;
	DSU dsu(n + 1);
	sort(query.begin(), query.end());
	for (auto [w, u, v, i] : query) {
		while (j < m && edge[j][0] <= w) {
			dsu.merge(edge[j][1], edge[j][2]);
			j++;
		}
		ans[i] = dsu.same(u, v);
	}
	for (int i = 0; i < q; i++) {
		if (ans[i])cout << "Yes" << endl;
		else cout << "No" << endl;
	}
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int t;
	cin >> t;
	while (t--)solve();
}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值