题目描述
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 ", 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 ", ans); } else { update(id[u], v, 1, n, 1); } } } return 0; }