[bzoj1036][ZJOI2008]树的统计Count 树链剖分

时间:2023-01-06 09:52:26

1036: [ZJOI2008]树的统计Count

Time Limit: 10 Sec   Memory Limit: 162 MB
[ Submit][ Status][ Discuss]

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

HINT

裸题,终于了解树链剖分了,下次写LCT
#include<iostream>
#include<cstring>
#include<cstdio>
#define INF 0x7fffffff
using namespace std;
const int N = 300000 + 5;
int n,q,sz,cnt,id,root,dep[N],v[N],last[N],fa[N],pos[N],bel[N],siz[N];
int ls[N*4],rs[N*4],sum[N*4],mx[N*4],l[N*4],r[N*4]; char ch[10];
struct Edge{ int to,next; }e[N*2];
void insert( int u, int v ){
e[++cnt].to = v; e[cnt].next = last[u]; last[u] = cnt;
e[++cnt].to = u; e[cnt].next = last[v]; last[v] = cnt;
}
void dfs1( int x ){
siz[x] = 1;
for( int i = last[x]; i; i = e[i].next ){
if( e[i].to == fa[x] ) continue;
dep[e[i].to] = dep[x]+1; fa[e[i].to] = x;
dfs1(e[i].to); siz[x] += siz[e[i].to];
}
}
void dfs2( int x, int chain ){
int k = 0; pos[x] = ++sz; bel[x] = chain;
for( int i = last[x]; i; i = e[i].next )
if( dep[e[i].to] > dep[x] && siz[e[i].to] > siz[k] )
k = e[i].to;
if( !k ) return ; dfs2(k,chain);
for( int i = last[x]; i; i = e[i].next )
if( e[i].to != k && dep[e[i].to] > dep[x] )
dfs2(e[i].to,e[i].to);
}
void build( int &k, int L, int R ){
l[++id] = L; r[id] = R; k = id;
if( L == R ) return ;
int mid = (L+R)>>1;
build(ls[k],L,mid); build(rs[k],mid+1,R);
}
void change( int k, int x, int y ){
int mid = (l[k]+r[k])>>1;
if( l[k] == r[k] ){ sum[k] = mx[k] = y; return; }
if( x <= mid ) change( ls[k], x, y );
else change( rs[k], x, y );
sum[k] = sum[ls[k]]+sum[rs[k]];
mx[k] = max(mx[ls[k]],mx[rs[k]]);
}
int query_sum( int k, int x, int y ){
if( l[k] == x && r[k] == y ) return sum[k];
int mid = (l[k]+r[k])>>1;
if( y <= mid ) return query_sum( ls[k], x, y );
if( x > mid ) return query_sum( rs[k], x, y );
return query_sum(ls[k],x,mid)+query_sum(rs[k],mid+1,y);
}
int query_mx( int k, int x, int y ){
if( l[k] == x && r[k] == y ) return mx[k];
int mid = (l[k]+r[k])>>1;
if( y <= mid ) return query_mx( ls[k], x, y );
if( x > mid ) return query_mx( rs[k], x, y );
return max(query_mx(ls[k],x,mid),query_mx(rs[k],mid+1,y));
}
int solve_sum( int x, int y ){
int res = 0;
while( bel[x] != bel[y] ){
if( dep[bel[x]] < dep[bel[y]] ) swap(x,y);
res += query_sum(root,pos[bel[x]],pos[x]);
x = fa[bel[x]];
}
if( pos[x] > pos[y] ) swap(x,y);
res += query_sum(root,pos[x],pos[y]);
return res;
}
int solve_mx( int x, int y ){
int res = -INF;
while( bel[x] != bel[y] ){
if( dep[bel[x]] < dep[bel[y]] ) swap(x,y);
res = max(res,query_mx(root,pos[bel[x]],pos[x]));
x = fa[bel[x]];
}
if( pos[x] > pos[y] ) swap(x,y);
res = max(res,query_mx(root,pos[x],pos[y]));
return res;
}
int main(){
scanf("%d", &n);
for( int i = 1,a,b; i < n; i++ ){
scanf("%d%d", &a, &b);
insert(a,b);
}
for( int i = 1; i <= n; i++ ) scanf("%d", &v[i]);
dfs1(1); dfs2(1,1); build(root,1,n);
for( int i = 1; i <= n; i++ ) change(root,pos[i],v[i]);
scanf("%d", &q);
for( int i = 1, x, y; i <= q; i++ ){
scanf("%s%d%d",ch,&x,&y);
if(ch[0]=='C'){ v[x] = y; change(1,pos[x],y); }
else{
if(ch[1]=='M') printf("%d\n",solve_mx(x,y));
else printf("%d\n",solve_sum(x,y));
}
}
return 0;
}