题意:一棵树,每个节点有初始权值,有p次操作,要么询问某个点的权值,要么修改树上两个节点u-v间路径的权值。
思路:树链剖分。这是我做的第一道树链剖分题,我对树链剖分的理解是,把树递归地分割成线性(重边优先),按顺序排列起来,最后长度和树的节点数一样。然后就可以利用线段树,对树上的路径进行维护。
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<iostream>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<string.h>
#include<cstdio>
using namespace std;
#define maxn 50010
int a[maxn];
int head[maxn];
int next[maxn*2];
int to[maxn*2];
int siz[maxn]; //以i为根的子树的节点数
int dep[maxn]; //节点深度
int top[maxn]; //所在链的顶端节点
int fa[maxn]; //父节点
int son[maxn]; //重儿子
int w[maxn]; //节点在线段树中的位置
int w_1[maxn];
bool vis[maxn];
int pos;
int end;
int n,m,p;
void init(int n){
for(int i=1;i<=n;i++){
siz[i]=top[i]=son[i]=0;
dep[i]=w[i]=w_1[i]=fa[i]=0;
head[i]=-1;
}
pos=0;
end=0;
}
void addedge(int u,int v){
to[end]=v,next[end]=head[u],head[u]=end++;
to[end]=u,next[end]=head[v],head[v]=end++;
}
void dfs(int x,int pre){
vis[x]=1;
dep[x]=dep[pre]+1;
fa[x]=pre;
for(int i=head[x];i!=-1;i=next[i]){
if(to[i]==pre)continue;
if(vis[to[i]])continue;
dfs(to[i],x);
siz[x]+=siz[to[i]];
if(son[x]==0)son[x]=to[i];
else if(siz[son[x]]<siz[to[i]])son[x]=to[i];
}
siz[x]++;
}
void dfs2(int x,int pre){
vis[x]=1;
pos++;
w[x]=pos;
w_1[pos]=x;
if(son[fa[x]]==x)top[x]=top[fa[x]];
else top[x]=x;
//先递归重儿子
if(son[x]==0)return;
dfs2(son[x],x);
for(int i=head[x];i!=-1;i=next[i]){
if(to[i]==pre)continue;
if(to[i]==son[x])continue;
if(vis[to[i]])continue;
dfs2(to[i],x);
}
}
struct node{
int l,r;
int val;
int lazy;
};
node tree[maxn<<2];
void build_tree(int n,int l,int r){
tree[n].l=l; tree[n].r=r;
tree[n].val=tree[n].lazy=0;
if(l==r){
tree[n].val=a[w_1[l]];
return;
}
int mid=(l+r)>>1;
build_tree(n<<1,l,mid);
build_tree((n<<1)|1,mid+1,r);
}
void push_down(int x){
int& add=tree[x].lazy;
node& lch=tree[x<<1];
node& rch=tree[(x<<1)|1];
if(lch.l==lch.r)lch.val+=add;
else lch.lazy+=add;
if(rch.l==rch.r)rch.val+=add;
else rch.lazy+=add;
add=0;
}
void update(int n,int l,int r,int v){
if(tree[n].l==l&&tree[n].r==r){
if(l==r){
tree[n].val+=v;
}else{
tree[n].lazy+=v;
}
return;
}
if(tree[n].lazy)push_down(n);
int mid=(tree[n].l+tree[n].r)>>1;
if(r<=mid){
update(n<<1,l,r,v);
}else{
if(l>mid){
update((n<<1)|1,l,r,v);
}else{
update(n<<1,l,mid,v);
update((n<<1)|1,mid+1,r,v);
}
}
}
int query(int n,int pos){
if(tree[n].l==tree[n].r)return tree[n].val;
if(tree[n].lazy)push_down(n);
int mid=(tree[n].l+tree[n].r)>>1;
if(pos<=mid) return query(n<<1,pos);
else return query((n<<1)|1,pos);
}
int main(){
while(cin>>n>>m>>p){
init(n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=1;i<=m;i++){
int u,v;
scanf("%d%d",&u,&v);
addedge(u,v);
}
memset(vis,0,sizeof(vis));
dfs(1,0);
memset(vis,0,sizeof(vis));
dfs2(1,0);
build_tree(1,1,n);
char op[2];
for(int i=1;i<=p;i++){
scanf("%s",op);
if(op[0]=='Q'){
int c;
scanf("%d",&c);
printf("%d\n",query(1,w[c]));
}else{
int u,v,k;
scanf("%d%d%d",&u,&v,&k);
if(op[0]=='D')k=-k;
//不在一条路径上,上爬
while(top[u]!=top[v]){//
if(dep[top[u]]<dep[top[v]])swap(u,v);
update(1,w[top[u]],w[u],k);
u=fa[top[u]];
}
//在一条路径上
if(w[u]>w[v])swap(u,v);
update(1,w[u],w[v],k);
}
}
}
return 0;
}