Ch’s gift
题意
给出结点数为n的一棵树,每个结点有权值;m 次询问,对于每次询问:a,b,l,r,求出结点a到结点b路径经过的点中,权值在在区间[l,r]内的权值和。
Sample Input
5 3
1 2 1 3 2
1 2
2 4
3 1
2 5
4 5 1 3
1 1 1 1
3 5 2 3
Sample Output
7 1 4
#include <cstdio>
#include <string>
#include <vector>
#include <algorithm>
#define N 100009
using namespace std;
vector<int> tree[N];
int father[N][21], h[N], val[N];
void dfs(int u, int p) {
h[u] = h[p] + 1;
father[u][0] = p;
// 20 个足够
for (int i = 1; i <= 20; i++) {
father[u][i] = father[father[u][i - 1]][i - 1];
}
for (int i = 0; i < tree[u].size(); i++) {
if (tree[u][i] == p) {
continue;
}
dfs(tree[u][i], u);
}
}
int lca(int u, int v) {
if (h[u] < h[v]) {
swap(u, v);
}
int d = h[u] - h[v];
for (int i = 0; i <= 20; i++) {
if ((d >> i) & 1) {
u = father[u][i];
}
}
if (u == v) {
return u;
}
for (int i = 20; i >= 0; i--)
if (father[u][i] != father[v][i]) {
u = father[u][i];
v = father[v][i];
}
return father[u][0];
}
int main() {
int n, m, a, b, l, r;
while (scanf("%d%d", &n, &m) != EOF) {
for (int i = 1; i <= n; i++) {
tree[i].clear();
}
for (int i = 1; i <= n; i++) {
scanf("%d", &val[i]);
}
for (int i = 0; i < n - 1; i++) {
scanf("%d%d", &a, &b);
tree[a].push_back(b);
tree[b].push_back(a);
}
dfs(1, 0);
while (m--) {
long long ans = 0;
scanf("%d%d%d%d", &a, &b, &l, &r);
int lc = lca(a, b);
for (int i = a; i != lc; i = father[i][0]) {
if (val[i] >= l && val[i] <= r) {
ans += val[i];
}
}
for (int i = b; i != lc; i = father[i][0]) {
if (val[i] >= l && val[i] <= r) {
ans += val[i];
}
}
if (val[lc] >= l && val[lc] <= r) {
ans += val[lc];
}
printf("%lld", ans);
if (m) {
printf(" ");
}
}
printf("\n");
}
return 0;
}