原题链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1036
树的统计
Description
一棵树上有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本身
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
题解
打板贼6
代码
#include<bits/stdc++.h>
#define ls son[v][0]
#define rs son[v][1]
using namespace std;
const int M=3e4+5;
int sum[M],mx[M],dad[M],son[M][2],val[M],data[M][2],n,m;
bool rev[M];
bool notroot(int v){return son[dad[v]][0]==v||son[dad[v]][1]==v;}
void up(int v){sum[v]=sum[ls]+sum[rs]+val[v];mx[v]=max(val[v],max(mx[ls],mx[rs]));}
void turn(int v){swap(ls,rs);rev[v]^=1;}
void down(int v){if(rev[v]){if(ls)turn(ls);if(rs)turn(rs);rev[v]=0;}}
void push(int v){if(notroot(v))push(dad[v]);down(v);}
void spin(int v)
{
int f=dad[v],ff=dad[f],k=son[f][1]==v,w=son[v][!k];
if(notroot(f))son[ff][son[ff][1]==f]=v;
son[v][!k]=f;son[f][k]=w;
if(w)dad[w]=f;
dad[f]=v;dad[v]=ff;
up(f);up(v);
}
void splay(int v)
{
push(v);
int f,ff;
while(notroot(v))
{
f=dad[v];ff=dad[f];
if(notroot(f))spin((son[f][0]==v)^(son[ff][0]==f)?v:f);
spin(v);
}
}
void access(int v){for(int f=0;v;v=dad[f=v])splay(v),rs=f,up(v);}
void beroot(int v){access(v);splay(v);turn(v);}
void split(int x,int y){beroot(x);access(y);splay(y);}
void link(int x,int y){beroot(x);dad[x]=y;}
void in()
{
int a,b;
scanf("%d",&n);
for(int i=1;i<n;++i)
scanf("%d%d",&data[i][0],&data[i][1]);
for(int i=1;i<=n;++i)
scanf("%d",&a),val[i]=sum[i]=mx[i]=a;
for(int i=1;i<n;++i)
link(data[i][0],data[i][1]);
}
void ac()
{
mx[0]=-1e9;
char ch[10];
int a,b;
scanf("%d",&m);
for(int i=1;i<=m;++i)
{
scanf("%s%d%d",ch,&a,&b);
switch(ch[1])
{
case 'H':splay(a);val[a]=b;up(a);break;
case 'M':split(a,b);printf("%d\n",mx[b]);break;
case 'S':split(a,b);printf("%d\n",sum[b]);
}
}
}
int main()
{
in();ac();
return 0;
}

本文介绍了一种使用线段树和树链剖分的技术来高效处理树形结构上的查询和更新操作的方法。具体实现了节点权值更改、路径最大权值查询及路径权值和查询等功能,并提供了完整的代码实现。
696

被折叠的 条评论
为什么被折叠?



