题意
给定一棵树,有 个点,根节点为
,每个节点有一个值
。
有 个询问,每个询问包含
,问当前值为
,从
走到
,每次遇到比当前值大的值,即将当前值替换为该值,最终总共要替换多少次。(
在
到根节点的路径上)
分析
这题还是很有必要记录一下的!
首先我们每次可以在 后面接一个点
,
点的值为
,相当于每次从
走向
。
然后暴力是 ,显然不可取。
但是如果我们能快速知道每个值往上走第一个比它大的值,就可以倍增来解决了。
问题是,怎么获取每个值往上走的第一个比它大的值呢?
注意到,比 大的第一个值,一定是
或这比
大的第
个值(
为正整数,这里假设
的值为无穷大)。
于是,我们可以用倍增来求比 大的第一个点。
就是每次从父节点往上跑,如果某一段都比 小,那么答案肯定不在这一段里,否则答案肯定在这一段里。
有点二分的感觉。
处理完 表,回答询问就很简单了。就是深度满足就往上爬。
总复杂度
代码如下
#include <bits/stdc++.h>
#define N 300005
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
LL z = 1;
struct node{
int a, b, n;
}d[N * 2];
int h[N], fa[N], w[N], q[N][2], st[N][20], dep[N], cnt;
void cr(int a, int b){
d[++cnt].a = a; d[cnt].b = b; d[cnt].n = h[a]; h[a] = cnt;
}
int read(){
int x, f = 1;
char ch;
while(ch = getchar(), ch < '0' || ch > '9') if(ch == '-') f = -1;
x = ch - '0';
while(ch = getchar(), ch >= '0' && ch <= '9') x = x * 10 + ch - 48;
return x * f;
}
void dfs(int a){
int i, b, t;
if(w[a] < w[fa[a]]) st[a][0] = fa[a];
else{
t = fa[a];
for(i = 19; i >= 0; i--){
if(st[t][i] && w[st[t][i]] <= w[a]) t = st[t][i];
}
st[a][0] = st[t][0];
}
for(i = 1; i <= 19; i++) st[a][i] = st[st[a][i - 1]][i - 1];
for(i = h[a]; i; i = d[i].n){
b = d[i].b;
if(b == fa[a]) continue;
fa[b] = a;
dep[b] = dep[a] + 1;
dfs(b);
}
}
int main(){
int i, j, n, m, a, b, ans = 0;
n = read(); m = read();
for(i = 1; i <= n; i++) w[i] = read();
for(i = 1; i < n; i++){
a = read(); b = read();
cr(a, b);
cr(b, a);
}
for(i = 1; i <= m; i++){
q[i][0] = read(); q[i][1] = read(); w[i + n] = read();
cr(i + n, q[i][0]);
cr(q[i][0], i + n);
}
dep[1] = 1;
dfs(1);
for(i = 1; i <= m; i++){
ans = 0;
a = i + n, b = q[i][1];
for(j = 19; j >= 0; j--){
if(dep[st[a][j]] >= dep[b]){
a = st[a][j];
ans += (1 << j);
}
}
printf("%d\n", ans);
}
return 0;
}
京公网安备 11010502036488号