G. Vlad and the Mountains
G. Vlad and the Mountains
Vlad decided to go on a trip to the mountains. He plans to move between mountains, some of which are connected by roads. The -th mountain has a height of .
If there is a road between mountains and , Vlad can move from mountain to mountain by spending units of energy. If his energy drops below zero during the transition, he will not be able to move from mountain to mountain . Note that 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 and ending at mountain , given that he initially has units of energy?
Input
The first line of the input contains an integer () — the number of test cases.
The descriptions of the test cases follow.
The first line of each test case contains two numbers and (, ) — the number of mountains and the number of roads between them, respectively.
The second line contains integers () — the heights of the mountains.
The next lines contain two integers and (, ) — the numbers of the mountains connected by a road. It is guaranteed that no road appears twice.
The next line contains an integer () — the number of queries.
The following lines contain three numbers , , and (, ) — the initial and final mountains of the route, and the amount of energy, respectively.
It is guaranteed that the sum of over all test cases does not exceed . The same guarantee applies to and .
Output
For each query, output "YES" if Vlad can construct a route from mountain to mountain , 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
解题思路
首先要发现这样一个性质,对于路径,消耗的能量为,即从到的路径中消耗的能量只取决于起点和终点的高度。现在的问题是给定初始能力,问是否存在一条从到的路径。很明显至少要满足才可能存在从到的路径,即要满足。再考虑路径中经过的山脉高度,事实上只要路径中的所有点都满足,那么就一定是一条从到的路径,因为这样就可以保证不会出现经过下一个点后能量小于的情况。反证法,假设当前到达点,消耗的能量是,并且从到的过程中能量始终不低于,如果从到下一个点消耗的能量超过到达时剩余的能量,即,即有,这就与矛盾了。
因此如果给定起点,终点和初始能量,将图中所有高度不超过的点选择出来,若选出的某两个点存在边相连则将这两个点所在连通块进行合并,最后如果和在一个连通块,则说明存在一条从到的路径。如果直接枚举每个询问然后都按照上面的说法来做,那么时间复杂度就是,肯定会超时。可以发现对于每个询问我们每次都是找出小于某个值的点,因此可以离线处理询问,根据关键字从小到大枚举每个询问,并且依次把高度不超过的点加到连通块中,这样就大大减少了重复加入的过程。
具体做法是对每个点按照高度进行升序排序,同时对每个询问按照关键字进行升序排序。然后按照顺序依次枚举询问,用一个指针维护已加到哪个点,当枚举到排序后的第个询问,那么就从指针开始往后枚举,把所有高度不超过的点都加到连通块中(即枚举与该点相连的所有边,若边的另一个端点的高度也不超过,这合并这两个点所在的连通块),最后看看和是否在同一个连通块。
AC代码如下,时间复杂度为:
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
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/17622694.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效