题目:BZOJ4034、洛谷P3178。
题目大意:有一棵点数为 N 的树,以点 1 为根,且树有点权。然后有 M 个操作,分为三种:
操作 1 :把某个节点 x 的点权增加 a 。
操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。
操作 3 :询问某个节点 x 到根的路径中所有点的点权和。
现在要你输出所有询问的答案。
解题思路:对树上的节点修改查询,很容易想到树链剖分。
单点修改就相当于只包含一个节点的区间修改。
子树修改,由于树链剖分中一棵子树的dfs序连续,相当于区间修改。
查询就相当于多次区间查询。
树链剖分时间复杂度$O(nlog^2 n)$。
C++ Code:
#include<cstdio> #include<cctype> #include<cstring> #define N 100005 #define mem(a) memset(&a,0,sizeof a) #define ll long long int n,m,a[N],aa[N],dfn[N],idx,cnt,head[N],sz[N],fa[N],top[N],son[N],dep[N]; ll ans; struct edge{ int to,nxt; }e[N<<1]; struct SegmentTreeNode{ ll s,add; }d[N<<2]; inline int readint(){ char c=getchar(); bool b=false; for(;!isdigit(c);c=getchar())b=c=='-'; int d=0; for(;isdigit(c);c=getchar()) d=(d<<3)+(d<<1)+(c^'0'); return b?-d:d; } void dfs(int now){ sz[now]=1; for(int i=head[now];i;i=e[i].nxt) if(!dep[e[i].to]){ dep[e[i].to]=dep[now]+1; fa[e[i].to]=now; dfs(e[i].to); sz[now]+=sz[e[i].to]; if(!son[now]||sz[e[i].to]>sz[son[now]]) son[now]=e[i].to; } } void dfs2(int now){ dfn[now]=++idx; if(son[now])top[son[now]]=top[now],dfs2(son[now]); for(int i=head[now];i;i=e[i].nxt) if(dep[e[i].to]>dep[now]&&e[i].to!=son[now]) top[e[i].to]=e[i].to,dfs2(e[i].to); } inline void pushdown(int len,int o){ int l=o<<1,r=o<<1|1; d[l].add+=d[o].add; d[r].add+=d[o].add; d[l].s+=d[o].add*((len+1)>>1); d[r].s+=d[o].add*(len>>1); d[o].add=0; } void build(int l,int r,int o){ if(l==r){ d[o].s=aa[l]; return; } int mid=(l+r)>>1; build(l,mid,o<<1); build(mid+1,r,o<<1|1); d[o].s=d[o<<1].s+d[o<<1|1].s; } void add(int l,int r,int o,int L,int R,int k){ if(L<=l&&r<=R){ d[o].add+=k; d[o].s+=(ll)k*(r-l+1); return; } pushdown(r-l+1,o); int mid=(l+r)>>1; if(L<=mid)add(l,mid,o<<1,L,R,k); if(mid<R)add(mid+1,r,o<<1|1,L,R,k); d[o].s=d[o<<1].s+d[o<<1|1].s; } void query(int l,int r,int o,int L,int R){ if(L<=l&&r<=R){ ans+=d[o].s; return; } pushdown(r-l+1,o); int mid=(l+r)>>1; if(L<=mid)query(l,mid,o<<1,L,R); if(mid<R)query(mid+1,r,o<<1|1,L,R); } void que(int x){ while(top[x]!=1){ query(1,n,1,dfn[top[x]],dfn[x]); x=fa[top[x]]; } query(1,n,1,dfn[1],dfn[x]); } int main(){ idx=cnt=0; n=readint(),m=readint(); mem(dfn);mem(head);mem(sz);mem(fa);mem(top);mem(son);mem(dep);mem(d); for(int i=1;i<=n;++i)a[i]=readint(); for(int i=1;i<n;++i){ int u=readint(),v=readint(); e[++cnt]=(edge){v,head[u]}; head[u]=cnt; e[++cnt]=(edge){u,head[v]}; head[v]=cnt; } fa[1]=top[1]=dep[1]=1; dfs(1);dfs2(1); for(int i=1;i<=n;++i)aa[dfn[i]]=a[i]; build(1,n,1); while(m--){ int f=readint(); if(f==1){ int x=readint(),k=readint(); add(1,n,1,dfn[x],dfn[x],k); }else if(f==2){ int x=readint(),k=readint(); add(1,n,1,dfn[x],dfn[x]+sz[x]-1,k); }else{ int x=readint(); ans=0; que(x); printf("%lld ",ans); } } return 0; }