【树链剖分】[BZOJ1036][ZJOI2008]树的统计Count

时间:2022-06-01 20:09:19

题目描述

一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 III. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身

输入的第一行为一个整数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之间。

对于每个“QMAX”或者“QSUM”的操作,每行输出一个整数表示要求输出的结果。

样例输入

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

样例输出

4
1
2
2
10
6
5
6
5
16

题目分析

我们可以通过观察题目发现emoji难道这不是一个裸的树链剖分么。。。。。。每次操作复杂度 O(logN) 。。总复杂度 O(QlogN) 。。。这数据刚刚好。。写树链剖分吧,我记得树链剖分是可以用来求LCA的,就按照那个方法搞一搞就好了

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 30000;
const int MAXQ = 200000;
struct node{
int v;
node *next;
}Edges[MAXN*2+10], *ecnt=Edges, *adj[MAXN+10];
int Maxson[MAXN+10], sz[MAXN+10], id[MAXN+10], idcnt, top[MAXN+10], vals[MAXN+10], Dep[MAXN+10], Fa[MAXN+10], revid[MAXN+10];
void addedge(int u, int v){
++ecnt;
ecnt->v = v;
ecnt->next = adj[u];
adj[u] = ecnt;
}
void dfs(int u, int fa){
sz[u]=1;
Fa[u] = fa;
Dep[u]=Dep[fa]+1;
for(node *p=adj[u];p;p=p->next){
if(p->v == fa) continue;
dfs(p->v, u);
sz[u] += sz[p->v];
if(sz[p->v] > sz[Maxson[u]])
Maxson[u] = p->v;
}
}
void dfs2(int u, int tp, int fa){
top[u] = tp;
revid[(id[u]=++idcnt)]=u;
if(Maxson[u]) dfs2(Maxson[u], tp, u);
for(node *p=adj[u];p;p=p->next){
if(p->v==fa||p->v==Maxson[u]) continue;
dfs2(p->v, p->v, u);
}
}
struct Tree{
int Max, Sum;
}tree[MAXN*4+10];
void push_up(int u){
tree[u].Max = max(tree[u*2].Max, tree[u*2+1].Max);
tree[u].Sum = tree[u*2].Sum + tree[u*2+1].Sum;
}
void Update(int u, int l, int r, int p, int v){
if(l == r){
tree[u].Max = tree[u].Sum = v;
return ;
}else{
int mid = (l + r) >> 1;
if(p<=mid)Update(u*2, l, mid, p, v);
else Update(u*2+1, mid+1, r, p, v);
push_up(u);
}
}
int sumq(int u, int l, int r, int ql, int qr){
if(ql <= l && r <= qr)
return tree[u].Sum;
else if(ql > r || qr < l)
return 0;
int mid = (l + r) >
> 1;
if(ql > mid) return sumq(u*2+1, mid+1, r, ql, qr);
else if(qr <= mid) return sumq(u*2, l, mid, ql, qr);
return sumq(u*2, l, mid, ql, qr) + sumq(u*2+1, mid+1, r, ql, qr);
}
int maxq(int u, int l, int r, int ql, int qr){
if(ql <= l && r <= qr) return tree[u].Max;
int mid = (l + r) >
> 1;
if(ql > mid) return maxq(u*2+1, mid+1, r, ql, qr);
else if(qr <= mid) return maxq(u*2, l, mid, ql, qr);
return max(maxq(u*2, l, mid, ql, qr), maxq(u*2+1, mid+1, r, ql, qr));
}
int Qsum(int l, int r){
int f1 = top[l], f2 = top[r], ret = 0;
while(f1 != f2){
if(Dep[f1] < Dep[f2]){
swap(f1, f2);
swap(l, r);
}
ret += sumq(1, 1, idcnt, id[f1], id[l]);
l = Fa[f1];
f1 = top[l];
}
if(Dep[l] >
Dep[r])
swap(l, r);
return ret + sumq(1, 1, idcnt, id[l], id[r]);
}
int Qmax(int l, int r){
int f1 = top[l], f2 = top[r], ret = -1000000000;
while(f1 != f2){
if(Dep[f1] < Dep[f2]){
swap(f1, f2);
swap(l, r);
}
ret = max(ret, maxq(1, 1, idcnt, id[f1], id[l]));
l = Fa[f1];
f1 = top[l];
}
if(Dep[l] > Dep[r])
swap(l, r);
return max(ret, maxq(1, 1, idcnt, id[l], id[r]));
}
void dfs3(int u, int l, int r){
if(l == r){
tree[u].Max = tree[u].Sum = vals[revid[l]];
return ;
}
int mid = (l + r) >> 1;
dfs3(u*2, l, mid);
dfs3(u*2+1, mid+1, r);
push_up(u);
}
int main(){
int n, u, v;
scanf("%d", &n);
for(int i=1;i<n;i++){
scanf("%d%d", &u, &v);
addedge(u, v);
addedge(v, u);
}
dfs(1, 0);
dfs2(1, 1, 0);
for(int i=1;i<=n;i++)
scanf("%d", &vals[i]);
dfs3(1, 1, idcnt);
int q, a, b;
char ord[40];
scanf("%d", &q);
while(q--){
scanf("%s%d%d", ord, &a, &b);
if(ord[0] == 'C')
Update(1, 1, idcnt, id[a], b);
else{
if(ord[1] == 'S')
printf("%d\n", Qsum(a, b));
else
printf("%d\n", Qmax(a, b));
}
}

return 0;
}