题目链接
题意:给定一颗树,树上每一个节点都有一个权值,然后有q次查询,
I a b c a节点和b节点之间的路径上的点的权值加上c
D a b c a节点和b节点之间的路径上的点的权值减上c
Q a 查询a节点的权值。
树链剖分是将树按照重链分成许多链,变成一个线性结构,这样就可以直接用线段树等数据结构处理了,每两个点之间链的条数不会超过log(n)条,如果用线段树处理的话,每一次的复杂度最多是log(n)*log(n)。
名词解释:重链,u的子节点为v,size[u]代表以u为根节点的子树节点数,则u的子节点最大的size[v]对应的这个v点就是u的重儿子,u->v之间的链就代表一条重链,然后将所有的重链链接起来重新编号解会变成一个线性结构。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
const int maxn=2e5+10;
int w[maxn],head[maxn],cont,tim,n,m,q;
void init()
{
memset(head,-1,sizeof(head));
cont=0;
tim=0;
}
struct zp
{
int u,v,next;
}node[maxn];
void add(int u,int v)
{
node[cont].u=u,node[cont].v=v;
node[cont].next=head[u];
head[u]=cont++;
}
// 节点大小 节点深度 重儿子 节点所在链的顶端节点 节点父节点 节点剖分后的新节点编号 线段树中的节点代表的原树中节点号
int siz[maxn],dep[maxn],son[maxn],top[maxn], fa[maxn], id[maxn], val[maxn];
void dfs1(int u,int f,int d)//处理处重链
{
son[u]=-1;
dep[u]=d;
siz[u]=1;
fa[u]=f;
for(int i=head[u];i+1;i=node[i].next)
{
int v=node[i].v;
if(v==f) continue;
dfs1(v,u,d+1);
siz[u]+=siz[v];
if(son[u]==-1||siz[v]>siz[son[u]])
son[u]=v;
}
}
void dfs2(int u,int tp)//给重链编号对应到线段树上
{
top[u]=tp;
id[u]=++tim;
val[id[u]]=u;
if(son[u]==-1) return ;
dfs2(son[u],tp);
for(int i=head[u];i+1;i=node[i].next)
{
int v=node[i].v;
if(v!=son[u]&&v!=fa[u])
dfs2(v,v);
}
}
struct tree
{
int lazy,val;
}T[maxn];
void push_down(int k)
{
if(T[k].lazy)
{
T[k<<1].val+=T[k].lazy;
T[k<<1|1].val+=T[k].lazy;
T[k<<1].lazy+=T[k].lazy;
T[k<<1|1].lazy+=T[k].lazy;
T[k].lazy=0;
}
}
void build_tree(int l,int r,int k)
{
T[k].lazy=0,T[k].val=0;
if(l==r)
{
T[k].val=w[val[l]];
return ;
}
int mid=(l+r)>>1;
build_tree(l,mid,k<<1);
build_tree(mid+1,r,k<<1|1);
}
void update(int l,int r,int ql,int qr,int k,int val)
{
if(l==ql&&r==qr)
{
T[k].val+=val;
T[k].lazy+=val;
return ;
}
push_down(k);
int mid=(l+r)>>1;
if(qr<=mid)
update(l,mid,ql,qr,k<<1,val);
else if(ql>mid)
update(mid+1,r,ql,qr,k<<1|1,val);
else
update(l,mid,ql,mid,k<<1,val),update(mid+1,r,mid+1,qr,k<<1|1,val);
}
int query(int l,int r,int k,int id)
{
if(l==r)
return T[k].val;
push_down(k);
int mid=(l+r)>>1;
if(id<=mid) return query(l,mid,k<<1,id);
else return query(mid+1,r,k<<1|1,id);
}
void chang(int x,int y,int val)//修改x到y路径节点上的值
{
while(top[x]!=top[y])//x和y不在同一条重链上
{
if(dep[top[x]]<dep[top[y]]) swap(x,y);//将深度深的节点道这条重链顶端的区间更新
update(1,n,id[top[x]],id[x],1,val);
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
update(1,n,id[x],id[y],1,val);//当在同一条重链上时更新x到y之间的这段区间就好了
}
int main()
{
while(~scanf("%d%d%d",&n,&m,&q))
{
init();
for(int i=1;i<=n;i++)
scanf("%d",&w[i]);
for(int i=0;i<m;i++)
{
int a,b;
scanf("%d%d",&a,&b);
add(a,b);
add(b,a);
}
dfs1(1,0,0);//剖树链
dfs2(1,1);//剖树链
build_tree(1,n,1);//建立线段树
while(q--)//根据线段树求解
{
char c;
scanf(" %c",&c);
if(c=='Q')
{
int a;
scanf("%d",&a);
printf("%d\n",query(1,n,1,id[a]));
}
else
{
int a,b,v;
scanf("%d%d%d",&a,&b,&v);
if(c=='D') v=-v;
chang(a,b,v);
}
}
}
}