题目描述
kotomi 有一棵树。树上有n个房子,编号1-n,每个房子有一个快乐值。
kotomi想知道从a房子到b房子路径上的最大快乐值或者路径山疙瘩快乐值的和。
并且kotomi可以改变任意一个房子的快乐值。
具体如下
(1) 0 a b:查询a到b路径上的最大快乐值(包含a和b)
(2) 1 a b:查询a到b路径上的所有房子快乐值的和。(包含a和b)
(3) 2 x y:将编号为x的房子的快乐值改为y。
输入描述:
多组测试数据
第一行有两个整数n,q。表示有n个房子,q次操作。
第二行有n个整数v1,v2...vn,表示编号为i的房子的快乐值vi
接下来n-1行,每行两个整数u,v,表示编号为u和编号为v的房子之间有一条直接路径。
接下来p行,每行开头一个整数(0,1或2)表述操作类型,每个操作后有两个整数。

输出描述:
对于操作为0和1的输出对应的值。
示例1
输入
6 10 2 5 9 10 36 5 1 2 1 3 1 4 2 5 2 6 0 1 4 0 1 6 1 5 6 1 3 6 1 6 3 2 3 10 1 5 3 0 4 5 2 5 100 1 5 4
输出
10 5 46 21 21 53 36 117
题解
裸的树链剖分。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 200000 + 10;
int n, q, v[maxn];
int h[maxn], to[maxn], nx[maxn], cnt;
int dep[maxn], son[maxn], top[maxn], sz[maxn], fa[maxn], id[maxn], fid[maxn];
int tot;
int sum[maxn * 4];
int mx[maxn * 4];
void add(int u, int v) {
to[cnt] = v;
nx[cnt] = h[u];
h[u] = cnt ++;
}
void dfs1(int x, int y, int d) {
dep[x] = d;
sz[x] = 1;
fa[x] = y;
for(int i = h[x]; i != -1; i = nx[i]) {
if(dep[to[i]]) continue;
dfs1(to[i], x, d + 1);
sz[x] = sz[x] + sz[to[i]];
if(sz[to[i]] > sz[son[x]]) son[x] = to[i];
}
}
void dfs2(int x) {
if(son[x]) {
top[son[x]] = top[x];
id[son[x]] = ++ tot;
fid[tot] = son[x];
dfs2(son[x]);
}
for(int i = h[x]; i != -1; i = nx[i]) {
if(top[to[i]]) continue;
if(to[i] == son[x]) continue;
top[to[i]] = to[i];
id[to[i]] = ++ tot;
fid[tot] = to[i];
dfs2(to[i]);
}
}
void pushUp(int rt) {
mx[rt] = max(mx[2 * rt], mx[2 * rt + 1]);
sum[rt] = sum[2 * rt] + sum[2 * rt + 1];
}
void build(int l, int r, int rt) {
if(l == r) {
sum[rt] = v[fid[l]];
mx[rt] = v[fid[l]];
return;
}
int mid = (l + r) / 2;
build(l, mid, 2 * rt);
build(mid + 1, r, 2 * rt + 1);
pushUp(rt);
}
void update(int pos, int val, int l, int r, int rt) {
if(l == r) {
sum[rt] = val;
mx[rt] = val;
return;
}
int mid = (l + r) / 2;
if(pos <= mid) update(pos, val, l, mid, 2 * rt);
else update(pos, val, mid + 1, r, 2 * rt + 1);
pushUp(rt);
}
int getsum(int L, int R, int l, int r, int rt) {
if(L <= l && r <= R) {
return sum[rt];
}
int mid = (l + r) / 2;
int left = 0;
int right = 0;
if(L <= mid) left = getsum(L, R, l, mid, 2 * rt);
if(R > mid) right = getsum(L, R, mid + 1, r, 2 * rt + 1);
return left + right;
}
int getmax(int L, int R, int l, int r, int rt) {
if(L <= l && r <= R) {
return mx[rt];
}
int mid = (l + r) / 2;
int left = -50000;
int right = -50000;
if(L <= mid) left = getmax(L, R, l, mid, 2 * rt);
if(R > mid) right = getmax(L, R, mid + 1, r, 2 * rt + 1);
return max(left, right);
}
int main() {
while(~scanf("%d%d", &n, &q)) {
for(int i = 1; i <= n; i ++) {
scanf("%d", &v[i]);
h[i] = -1;
}
for(int i = 0; i <= n; i ++) {
dep[i] = 0;
sz[i] = 0;
son[i] = 0;
top[i] = 0;
}
tot = cnt = 0;
for(int i = 1; i <= n - 1; i ++) {
int u, v;
scanf("%d%d", &u, &v);
add(u, v);
add(v, u);
}
dfs1(1, -1, 1);
tot = 0;
top[1] = 1;
id[1] = ++ tot;
fid[tot] = 1;
dfs2(1);
build(1, n, 1);
while(q --) {
int op, u, v;
scanf("%d%d%d", &op, &u, &v);
if(op == 0) {
int ans = -50000;
while(1) {
int f1 = top[u], f2 = top[v];
if(f1 != f2) {
if(dep[f1] >= dep[f2]) {
ans = max(ans, getmax(min(id[u], id[f1]), max(id[u], id[f1]), 1, n, 1));
u = fa[f1];
} else {
ans = max(ans, getmax(min(id[v], id[f2]), max(id[v], id[f2]), 1, n, 1));
v = fa[f2];
}
} else {
ans = max(ans, getmax(min(id[u], id[v]), max(id[u], id[v]), 1, n, 1));
break;
}
}
printf("%d\n", ans);
} else if(op == 1) {
int ans = 0;
while(1) {
int f1 = top[u], f2 = top[v];
if(f1 != f2) {
if(dep[f1] >= dep[f2]) {
ans = ans + getsum(min(id[u], id[f1]), max(id[u], id[f1]), 1, n, 1);
u = fa[f1];
} else {
ans = ans + getsum(min(id[v], id[f2]), max(id[v], id[f2]), 1, n, 1);
v = fa[f2];
}
} else {
ans = ans + getsum(min(id[u], id[v]), max(id[u], id[v]), 1, n, 1);
break;
}
}
printf("%d\n", ans);
} else {
update(id[u], v, 1, n, 1);
}
}
}
return 0;
}