Description
一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。我们将以下面的形式来要求你对这棵树完成
一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 I
II. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身
Input
输入的第一行为一个整数n,表示节点的个数。接下来n – 1行,每行2个整数a和b,表示节点a和节点b之间有
一条边相连。接下来n行,每行一个整数,第i行的整数wi表示节点i的权值。接下来1行,为一个整数q,表示操作
的总数。接下来q行,每行一个操作,以“CHANGE u t”或者“QMAX u v”或者“QSUM u v”的形式给出。
对于100%的数据,保证1<=n<=30000,0<=q<=200000;中途操作中保证每个节点的权值w在-30000到30000之间。
Output
对于每个“QMAX”或者“QSUM”的操作,每行输出一个整数表示要求输出的结果。
Sample Input
4
1 2
2 3
4 1
4 2 1 3
12
QMAX 3 4
QMAX 3 3
QMAX 3 2
QMAX 2 3
QSUM 3 4
QSUM 2 1
CHANGE 1 5
QMAX 3 4
CHANGE 3 6
QMAX 3 4
QMAX 2 4
QSUM 3 4
Sample Output
4
1
2
2
10
6
5
6
5
16
题解
先树链剖分,再用线段树维护区间最值、区间和,支持单点修改即可。判断一条链相交的点就top一直跳跳跳……跳到一个链上就行了。
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100010;
int n, m, num, root, a[N], head[N];
int idy, indx, siz[N], fa[N], dep[N], son[N], in[N], out[N], val[N], seq[N], top[N];
struct Edge {
int v, next;
} edge[N];
struct Node {
int l, r, sum, maxx;
} t[N];
void add(int u, int v) {
num ++;
edge[num].v = v;
edge[num].next = head[u];
head[u] = num;
}
void dfs1(int u, int f, int d) {
siz[u] = 1, fa[u] = f, dep[u] = d;
for(int i = head[u]; i; i = edge[i].next) {
int v = edge[i].v;
if(v != f) {
dfs1(v, u, d + 1);
siz[u] += siz[v];
if(son[u] == -1 || siz[son[u]] < siz[v])
son[u] = v;
}
}
}
void dfs2(int u, int tp) {
idy ++;
in[u] = out[u] = idy, seq[idy] = u;
val[in[u]] = a[u]; top[u] = tp;
if(son[u] == -1) return ;
dfs2(son[u], tp);
for(int i = head[u]; i; i = edge[i].next) {
int v = edge[i].v;
if(son[u] != v && v != fa[u])
dfs2(v, v);
}
out[u] = idy;
}
void build(int root, int L, int R) {
t[root].l = L, t[root].r = R;
if(L == R) {
t[root].sum = val[L];
t[root].maxx = val[L];
return ;
}
int mid = (L + R) >> 1;
build(root << 1, L, mid);
build(root << 1 | 1, mid + 1, R);
t[root].sum = t[root << 1].sum + t[root << 1 | 1].sum;
t[root].maxx = max(t[root << 1].maxx, t[root << 1 | 1].maxx);
return ;
}
void modify(int root, int x, int delta) {
int l = t[root].l, r = t[root].r;
if(l == r && l == x) {
t[root].sum = delta;
t[root].maxx = delta;
return ;
}
int mid = (l + r) >> 1;
if(x <= mid)
modify(root << 1, x, delta);
else
modify(root << 1 | 1, x, delta);
t[root].sum = t[root << 1].sum + t[root << 1 | 1].sum;
t[root].maxx = max(t[root << 1].maxx, t[root << 1 | 1].maxx);
return ;
}
int query1(int root, int L, int R) {
int l = t[root].l, r = t[root].r;
if(L <= l && r <= R)
return t[root].sum;
int mid = (l + r) >> 1, rt = 0;
if(L <= mid)
rt += query1(root << 1, L, R);
if(R > mid)
rt += query1(root << 1 | 1, L, R);
t[root].sum = t[root << 1].sum + t[root << 1 | 1].sum;
return rt;
}
int query2(int root, int L, int R) {
int l = t[root].l, r = t[root].r;
if(L <= l && r <= R)
return t[root].maxx;
int mid = (l + r) >> 1, rt = -0x3ffffff;
if(L <= mid)
rt = max(rt, query2(root << 1, L, R));
if(R > mid)
rt = max(rt, query2(root << 1 | 1, L, R));
t[root].maxx = max(t[root << 1].maxx, t[root << 1 | 1].maxx);
return rt;
}
int anssum(int u, int v) {
int f1 = top[u], f2 = top[v];
int su = 0;
while(f1 != f2){
if(dep[f1] < dep[f2]) swap(f1, f2), swap(u, v);
su += query1(1, in[f1], in[u]);
u = fa[f1], f1= top[u];
}
if(dep[u] > dep[v])
swap(u, v);
su += query1(1, in[u], in[v]);
return su;
}
int ansmax(int u, int v) {
int f1 = top[u], f2 = top[v];
int ma = -0x3ffffff;
while(f1 != f2){
if(dep[f1] < dep[f2]) swap(f1, f2), swap(u, v);
ma = max(ma, query2(1, in[f1], in[u]));
u = fa[f1], f1= top[u];
}
if(dep[u] < dep[v])
swap(u, v);
ma = max(ma, query2(1, in[v], in[u]));
return ma;
}
int main() {
memset(son, -1, sizeof(son));
scanf("%d", &n);
for(register int i = 1; i < n; i ++) {
int u, v;
scanf("%d %d", &u, &v);
add(u, v);
add(v, u);
}
for(register int i = 1; i <= n; i ++)
scanf("%d", &a[i]);
dfs1(1, -1, 0); dfs2(1, 1);
scanf("%d", &m);
build(root = 1, 1, n);
while(m --) {
char s[10];
int u, v;
scanf("%s %d %d", s, &u, &v);
if(s[0] == 'Q') {
if(s[1] == 'S')
printf("%d\n", anssum(u, v));
else
printf("%d\n", ansmax(u, v));
}
else
modify(1, in[u], v);
}
return 0;
}