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 n mountains, some of which are connected by roads. The i-th mountain has a height of hi.

If there is a road between mountains i and j, Vlad can move from mountain i to mountain j by spending hjhi 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 hjhi 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 (1t104) — 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 (2n2105, 1mmin(n(n1)2,2105)) — the number of mountains and the number of roads between them, respectively.

The second line contains n integers h1,h2,h3,,hn (1hi109) — the heights of the mountains.

The next m lines contain two integers u and v (1u,vn, uv) — the numbers of the mountains connected by a road. It is guaranteed that no road appears twice.

The next line contains an integer q (1q2105) — the number of queries.

The following q lines contain three numbers a, b, and e (1a,bn, 0e109) — 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 2105. 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

 

解题思路

  首先要发现这样一个性质,对于路径ijk,消耗的能量为hjhi+hkhj=hkhi,即从ik的路径中消耗的能量只取决于起点和终点的高度。现在的问题是给定初始能力e,问是否存在一条从ab的路径。很明显至少要满足hbhae才可能存在从ab的路径,即hb要满足hbha+e。再考虑路径中经过的山脉高度,事实上只要路径中的所有点都满足hiha+e,那么就一定是一条从ab的路径,因为这样就可以保证不会出现经过下一个点后能量小于0的情况。反证法,假设当前到达点u,消耗的能量是huhae,并且从au的过程中能量始终不低于0,如果从u到下一个点v消耗的能量超过到达u时剩余的能量,即hvhu+huha>e,即有hvha>e,这就与hvhae矛盾了。

  因此如果给定起点a,终点b和初始能量e,将图中所有高度不超过ha+e的点选择出来,若选出的某两个点存在边相连则将这两个点所在连通块进行合并,最后如果ab在一个连通块,则说明存在一条从ab的路径。如果直接枚举每个询问然后都按照上面的说法来做,那么时间复杂度就是O(q(n+m)),肯定会超时。可以发现对于每个询问我们每次都是找出小于某个值的点,因此可以离线处理询问,根据关键字h[a[i]]+e[i]从小到大枚举每个询问,并且依次把高度不超过h[a[i]]+e[i]的点加到连通块中,这样就大大减少了重复加入的过程。

  具体做法是对每个点按照高度hi进行升序排序,同时对每个询问按照关键字h[a[i]]+e[i]进行升序排序。然后按照顺序依次枚举询问,用一个指针维护已加到哪个点,当枚举到排序后的第i个询问,那么就从指针开始往后枚举,把所有高度不超过h[a[i]]+e[i]的点都加到连通块中(即枚举与该点相连的所有边,若边的另一个端点的高度也不超过h[a[i]]+e[i],这合并这两个点所在的连通块),最后看看aibi是否在同一个连通块。

  AC代码如下,时间复杂度为O(m+nlogn+qlogq)

复制代码
 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

posted @   onlyblues  阅读(89)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示