题目大意
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;
}