题目描述
给出一棵树,树上每个点有个点权,有Q次查询,每次查询都会从一个点出发向根跳k次,然后在这k次中要求出最大点权和最小点权之差
样例输入
6
5 6 1 7 5 2
2 1
3 1
4 2
5 2
6 3
3
4 3
6 2
6 3
样例输出
0
0
4
思路
因为是要在一条链上跑嘛
所以我们考虑倍增
记录下区间的最大最小和答案,然后按题意倍增求答案就好了
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int n, Q;
int a[500005], log[500005];
int f[500005][25];
int fmin[500005][25];
int fmax[500005][25];
int fans[500005][25];
int main()
{
freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout);
memset(fmin, 0x3f, sizeof(fmin));
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
log[0] = -1;
for(int i = 1; i <= n; ++i)
log[i] = log[i >> 1] + 1;
for(int i = 1; i < n; ++i)
{
int x, y;
scanf("%d%d", &x, &y);
f[x][0] = y;
fmin[x][0] = min(a[x], a[y]);//记录初始的
fmax[x][0] = max(a[x], a[y]);
if(a[y] < a[x]) fans[x][0] = 0;
fans[x][0] = max(a[y] - a[x], 0);
}
for(int k = 1; k <= 17; ++k)
for(int i = 2; i <= n; ++i)
{
f[i][k] = f[f[i][k - 1]][k - 1];
fmin[i][k] = min(fmin[i][k - 1], fmin[f[i][k - 1]][k - 1]);
fmax[i][k] = max(fmax[i][k - 1], fmax[f[i][k - 1]][k - 1]);
fans[i][k] = max(fans[i][k - 1], fans[f[i][k - 1]][k - 1]);
int p = max(0, fmax[f[i][k - 1]][k - 1] - fmin[i][k - 1]);
fans[i][k] = max(fans[i][k], p);
}//倍增转移
scanf("%d", &Q);
while(Q--)
{
int x, k;
scanf("%d%d", &x, &k);
k--;
if(k == 0) {
printf("0\n");
continue;
}
int ans = -1e9;
int last = 1e9;
while(x && k >= 0)
{
int t = log[k];
ans = max(ans, fans[x][t]);
ans = max(ans, fmax[x][t] - last);
last = min(last, fmin[x][t]);
x = f[x][t];
k -= (1 << t);
}//向上倍增求
printf("%d\n", ans);
}
return 0;
}