G. Vlad and the Mountains
Vlad decided to go on a trip to the mountains. He plans to move between $n$ mountains, some of which are connected by roads. The $i$-th mountain has a height of $h_i$.
If there is a road between mountains $i$ and $j$, Vlad can move from mountain $i$ to mountain $j$ by spending $h_j - h_i$ units of energy. If his energy drops below zero during the transition, he will not be able to move from mountain $i$ to mountain $j$. Note that $h_j - h_i$ can be negative and then the energy will be restored.
Vlad wants to consider different route options, so he asks you to answer the following queries: is it possible to construct some route starting at mountain $a$ and ending at mountain $b$, given that he initially has $e$ units of energy?
Input
The first line of the input contains an integer $t$ ($1 \le t \le 10^4$) — the number of test cases.
The descriptions of the test cases follow.
The first line of each test case contains two numbers $n$ and $m$ ($2 \le n \le 2 \cdot 10^5$, $1 \le m \le \min(\frac{n\cdot(n - 1)}{2}, 2 \cdot 10^5)$) — the number of mountains and the number of roads between them, respectively.
The second line contains $n$ integers $h_1, h_2, h_3, \dots, h_n$ ($1 \le h_i \le 10^9$) — the heights of the mountains.
The next $m$ lines contain two integers $u$ and $v$ ($1 \le u, v \le n$, $u \ne v$) — the numbers of the mountains connected by a road. It is guaranteed that no road appears twice.
The next line contains an integer $q$ ($1 \le q \le 2 \cdot 10^5$) — the number of queries.
The following $q$ lines contain three numbers $a$, $b$, and $e$ ($1 \le a, b \le n$, $0 \le e \le 10^9$) — the initial and final mountains of the route, and the amount of energy, respectively.
It is guaranteed that the sum of $n$ over all test cases does not exceed $2 \cdot 10^5$. The same guarantee applies to $m$ and $q$.
Output
For each query, output "YES" if Vlad can construct a route from mountain $a$ to mountain $b$, and "NO" otherwise.
You can output the answer in any case (for example, the strings "yEs", "yes", "Yes", and "YES" will be recognized as a positive answer).
In the examples below, the answers for different test cases are separated by an empty line, which you do not need to output.
Examples
input
2 7 7 1 5 3 4 2 4 1 1 4 4 3 3 6 3 2 2 5 5 6 5 7 5 1 1 3 6 2 0 4 7 0 1 7 4 1 7 2 6 5 4 7 6 2 5 1 1 3 5 3 1 5 2 4 6 2 5 1 5 1 1 3 1 1 2 1000 6 2 6 6 2 5
output
YES NO YES YES NO YES NO NO YES NO
input
2 3 2 1 3 9 1 2 2 3 5 1 1 1 3 2 2 1 1 2 3 3 0 1 2 1 3 3 1 4 1 1 2 2 3 1 3 5 3 3 9 1 3 6 1 1 2 3 3 6 3 3 4
output
YES YES YES YES NO YES YES YES YES YES
input
1 6 10 7 9 2 10 8 6 4 2 6 1 4 5 3 5 6 4 1 3 2 6 6 5 1 2 3 6 5 4 4 8 3 3 1 5 5 9 2 1 7 6 6 10
output
YES YES YES YES YES
解题思路
首先要发现这样一个性质,对于路径$i \to j \to k$,消耗的能量为$h_j - h_i + h_k - h_j = h_k - h_i$,即从$i$到$k$的路径中消耗的能量只取决于起点和终点的高度。现在的问题是给定初始能力$e$,问是否存在一条从$a$到$b$的路径。很明显至少要满足$h_b - h_a \leq e$才可能存在从$a$到$b$的路径,即$h_b$要满足$h_b \leq h_a + e$。再考虑路径中经过的山脉高度,事实上只要路径中的所有点都满足$h_i \leq h_a + e$,那么就一定是一条从$a$到$b$的路径,因为这样就可以保证不会出现经过下一个点后能量小于$0$的情况。反证法,假设当前到达点$u$,消耗的能量是$h_u - h_a \leq e$,并且从$a$到$u$的过程中能量始终不低于$0$,如果从$u$到下一个点$v$消耗的能量超过到达$u$时剩余的能量,即$h_v - h_u + h_u - h_a > e$,即有$h_v - h_a > e$,这就与$h_v - h_a \leq e$矛盾了。
因此如果给定起点$a$,终点$b$和初始能量$e$,将图中所有高度不超过$h_a + e$的点选择出来,若选出的某两个点存在边相连则将这两个点所在连通块进行合并,最后如果$a$和$b$在一个连通块,则说明存在一条从$a$到$b$的路径。如果直接枚举每个询问然后都按照上面的说法来做,那么时间复杂度就是$O(q(n+m))$,肯定会超时。可以发现对于每个询问我们每次都是找出小于某个值的点,因此可以离线处理询问,根据关键字$h[a[i]] + e[i]$从小到大枚举每个询问,并且依次把高度不超过$h[a[i]] + e[i]$的点加到连通块中,这样就大大减少了重复加入的过程。
具体做法是对每个点按照高度$h_i$进行升序排序,同时对每个询问按照关键字$h[a[i]] + e[i]$进行升序排序。然后按照顺序依次枚举询问,用一个指针维护已加到哪个点,当枚举到排序后的第$i$个询问,那么就从指针开始往后枚举,把所有高度不超过$h[a[i]] + e[i]$的点都加到连通块中(即枚举与该点相连的所有边,若边的另一个端点的高度也不超过$h[a[i]] + e[i]$,这合并这两个点所在的连通块),最后看看$a_i$和$b_i$是否在同一个连通块。
AC代码如下,时间复杂度为$O(m + n\log{n} + q\log{q})$:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 typedef long long LL; 5 6 const int N = 2e5 + 10; 7 8 int h[N], p[N]; 9 int a[N], b[N], c[N], q[N]; 10 vector<int> g[N]; 11 int fa[N]; 12 bool ans[N]; 13 14 int find(int x) { 15 return fa[x] == x ? fa[x] : fa[x] = find(fa[x]); 16 } 17 18 void solve() { 19 int n, m; 20 scanf("%d %d", &n, &m); 21 for (int i = 1; i <= n; i++) { 22 scanf("%d", h + i); 23 p[i] = i; 24 g[i].clear(); 25 } 26 while (m--) { 27 int v, w; 28 scanf("%d %d", &v, &w); 29 g[v].push_back(w); 30 g[w].push_back(v); 31 } 32 scanf("%d", &m); 33 for (int i = 1; i <= m; i++) { 34 scanf("%d %d %d", a + i, b + i, c + i); 35 q[i] = i; 36 } 37 sort(p + 1, p + n + 1, [&](int i, int j) { // 山脉按照高度从小到大排序 38 return h[i] < h[j]; 39 }); 40 sort(q + 1, q + m + 1, [&](int i, int j) { // 询问按照关键字h[a[i]]+c[i]从小到大排序 41 return h[a[i]] + c[i] < h[a[j]] + c[j]; 42 }); 43 for (int i = 1; i <= n; i++) { 44 fa[i] = i; 45 } 46 memset(ans, 0, m + 10); 47 for (int i = 1, j = 1; i <= m; i++) { 48 while (j <= n && h[p[j]] <= h[a[q[i]]] + c[q[i]]) { // 把所有高度不超过h[a[q[i]]]+c[q[i]]的点加到连通块中 49 for (auto &x : g[p[j]]) { 50 if (h[x] <= h[p[j]]) fa[find(x)] = find(p[j]); // 边的另外一端的高度也要满足不超过h[a[q[i]]]+c[q[i]],因为此时连通块的点都不超过h[a[q[i]]]+c[q[i]] 51 } 52 j++; 53 } 54 ans[q[i]] = find(a[q[i]]) == find(b[q[i]]); // 查询起点和终点是否在同一个连通块中 55 } 56 for (int i = 1; i <= m; i++) { 57 printf("%s\n", ans[i] ? "YES" : "NO"); 58 } 59 } 60 61 int main() { 62 int t; 63 scanf("%d", &t); 64 while (t--) { 65 solve(); 66 } 67 68 return 0; 69 }
参考资料
Codeforces round #888 (Div. 3) Editorial:Codeforces round #888 (Div. 3) Editorial - Codeforces
标签:mountain,le,Vlad,int,YES,Mountains,10 From: https://www.cnblogs.com/onlyblues/p/17622694.html