[hihoCoder#1065]全图传送
[hihoCoder#1065]全图传送
试题描述
先知法里奥是 Dota 系列中的一个英雄。机动性强,推塔能力一流,打钱速度快,传送技能使先知可以全地图支援。在后期比拼中通过强大的兵线控制能力使得对方的兵线一直难以过河,并具有一定的后期 DPS 能力。
我们今天要考虑的是传送技能。假设战场是一个树形区域,每个节点有一个权值 vi。当你传送到某个点 u 时,可以支援距离点 u 半径 r 以内的所有节点。请对于下列询问"u r", 回答以 u 节点为中心,半径 r 以内的节点中,权值最大的节点的编号是多少。如果有多个节点,返回编号最小的。
输入
第一行包含一个整数 n (1 ≤ n ≤ 105),表示节点总数。
接下来的一行,包含 n 个数字,表示每个节点的权值 vi (1 ≤ vi ≤ 109)。 接下来的 n-1 行,每行三个整数 (ai, bi,wi),表示一条连接 ai, bi 节点的边,边长为 wi(1 ≤ ai, bi ≤ n, 1 ≤ wi ≤ 104)。
接下来的一行包含一个整数 q,表示询问总数(1 ≤ q ≤ 105)。 接下来 q 行,每行包含两个整数 u, r(1 ≤ u ≤ n, 0 ≤ r ≤ 109),表示询问以 u 节点为中心,半径 r 以内的节点中,权值最大的节点的编号是多少。如果有多解返回编号最小的。
输出
对于每组询问,输出一行表示对应答案。
输入示例
7 1 2 3 4 5 6 7 1 2 1 2 3 1 2 4 1 1 5 1 5 6 1 5 7 1 4 1 1 1 2 2 1 2 2
输出示例
5 7 4 5
数据规模及约定
见“输入”
题解
考虑离线处理每个操作,把操作所对应的节点记录到节点信息上,考虑使用点分治。显然可以递归每次减小问题规模,考虑跨中点的部分,我们 dfs 并记录每个节点的深度和权值,按照深度为关键词排序,处理一下前缀最大值,对于一个询问,我们二分一下更新答案即可。
#include <cstdio> #include <algorithm> #include <cmath> #include <cctype> #include <stack> #include <vector> #include <queue> #include <cstring> #include <string> #include <map> #include <set> using namespace std; const int BufferSize = 1 << 16; char buffer[BufferSize], *Head, *Tail; inline char Getchar() { if(Head == Tail) { int l = fread(buffer, 1, BufferSize, stdin); Tail = (Head = buffer) + l; } return *Head++; } int read() { int x = 0, f = 1; char c = getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); } return x * f; } #define maxn 100010 #define maxm 200010 #define oo 2147483647 int n, m, head[maxn], nxt[maxm], to[maxm], dist[maxm], val[maxn], ans[maxn]; void AddEdge(int a, int b, int c) { to[++m] = b; dist[m] = c; nxt[m] = head[a]; head[a] = m; swap(a, b); to[++m] = b; dist[m] = c; nxt[m] = head[a]; head[a] = m; return ; } int q, m2, h2[maxn], n2[maxn]; struct Que { int u, r; Que() {} Que(int _, int __): u(_), r(__) {} bool operator < (const Que& t) const { return r < t.r; } } qs[maxn]; bool vis[maxn]; int root, f[maxn], siz[maxn], size; void getroot(int u, int fa) { siz[u] = 1; f[u] = 0; for(int e = head[u]; e; e = nxt[e]) if(to[e] != fa && !vis[to[e]]) { getroot(to[e], u); siz[u] += siz[to[e]]; f[u] = max(f[u], siz[to[e]]); } f[u] = max(f[u], size - siz[u]); if(f[u] < f[root]) root = u; return ; } int ToT, ns[maxn]; Que A[maxn]; void dfs(int u, int d, int fa) { A[++ToT] = Que(u, d); for(int e = head[u]; e; e = nxt[e]) if(to[e] != fa && !vis[to[e]]) dfs(to[e], d + dist[e], u); return ; } void solve(int u) { // printf("u: %d\n", u); vis[u] = 1; A[ToT = 1] = Que(u, 0); for(int e = head[u]; e; e = nxt[e]) if(!vis[to[e]]) dfs(to[e], dist[e], u); sort(A + 1, A + ToT + 1); // for(int i = 1; i <= ToT; i++) printf("%d %d\n", A[i].u, A[i].r); for(int i = 1; i <= ToT; i++) ns[i] = A[i].u; for(int i = 2; i <= ToT; i++) if(val[A[i-1].u] > val[A[i].u]) A[i].u = A[i-1].u; else if(val[A[i-1].u] == val[A[i].u] && A[i-1].u < A[i].u) A[i].u = A[i-1].u; for(int i = 1; i <= ToT; i++) for(int u = h2[ns[i]]; u; u = n2[u]) { // printf("%d q[u]: %d\n", ns[i], u); Que& que = qs[u]; if(que.r < A[i].r) continue; int x = upper_bound(A + 1, A + ToT + 1, Que(0, que.r - A[i].r)) - A; if(x <= ToT && A[x].r <= que.r - A[i].r) x++; x--; if(ans[u] == oo) ans[u] = A[x].u; else if(val[ans[u]] < val[A[x].u]) ans[u] = A[x].u; else if(val[ans[u]] == val[A[x].u] && ans[u] > A[x].u) ans[u] = A[x].u; } for(int e = head[u]; e; e = nxt[e]) if(!vis[to[e]]) { root = 0; f[0] = n + 1; size = siz[to[e]]; getroot(to[e], u); solve(root); } return ; } int main() { n = read(); for(int i = 1; i <= n; i++) val[i] = read(); for(int i = 1; i < n; i++) { int a = read(), b = read(), c = read(); AddEdge(a, b, c); } q = read(); for(int i = 1; i <= q; i++) { int u = read(), r = read(); qs[i] = Que(u, r); n2[++m2] = h2[u]; h2[u] = m2; } for(int i = 1; i <= q; i++) ans[i] = oo; root = 0; f[0] = n + 1; size = n; getroot(1, 0); solve(root); for(int i = 1; i <= q; i++) printf("%d\n", ans[i]); return 0; }