「Luogu P3178」[HAOI2015]树上操作 发表于 2018-08-14 | 字数统计: 1.2k | 阅读时长 ≈ 6 给定一棵树和多个操作,对于每一个操作3,回答该询问的答案。 链接Luogu P3178BZOJ 4034 题解树链剖分板子题。对于每一个1操作,直接将起点和终点设为这个点,执行路径修改。对于操作2和操作3,直接使用树剖板子。 代码123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225#include<bits/stdc++.h>using namespace std;typedef long long int ll;struct edge{ ll to,prev;};struct SegmentTree{ ll l,r,sum,tag;};edge ed[200051];SegmentTree tree[200051<<2];ll last[200051],val[200051],depth[200051],fa[200051],size[200051],heavy[200051];ll id[200051],pre[200051],top[200051];ll tot,nc,cnt,ccnt,op,x,y,from,to;inline ll read()//快读{ register ll num=0,neg=1; register char ch=getchar(); while(!isdigit(ch)&&ch!='-') { ch=getchar(); } if(ch=='-') { neg=-1; ch=getchar(); } while(isdigit(ch)) { num=(num<<3)+(num<<1)+(ch-'0'); ch=getchar(); } return num*neg;}inline void addEdge(ll from,ll to)//邻接表基本操作{ ed[++tot].prev=last[from]; ed[tot].to=to; last[from]=tot;}//线段树基本操作inline void update(ll node)//更新节点值,用位运算优化{ tree[node].sum=tree[node<<1].sum+tree[(node<<1)|1].sum;}inline void create(ll l,ll r,ll node)//建树{ tree[node].l=l,tree[node].r=r; if(l==r) { tree[node].sum=val[l]; return; } ll mid=(l+r)>>1; create(l,mid,node<<1);//建左子树 create(mid+1,r,(node<<1)|1);//建右子树 update(node);//更新当前节点}inline void spread(ll node)//下传懒标记{ if(tree[node].tag) { //更新节点信息 tree[node<<1].sum+=tree[node].tag*(tree[node<<1].r-tree[node<<1].l+1); tree[(node<<1)|1].sum+=tree[node].tag*(tree[(node<<1)|1].r-tree[(node<<1)|1].l+1); //下传懒标记 tree[node<<1].tag+=tree[node].tag; tree[(node<<1)|1].tag+=tree[node].tag; //清除当前节点懒标记 tree[node].tag=0; }}inline void add(ll l,ll r,ll val,ll node){ if(l<=tree[node].l&&r>=tree[node].r) { tree[node].sum+=val*(tree[node].r-tree[node].l+1); //做标记 tree[node].tag+=val; return; } spread(node);//标记下传 ll mid=(tree[node].l+tree[node].r)>>1; if(l<=mid) { add(l,r,val,node<<1); } if(r>mid) { add(l,r,val,(node<<1)|1); } update(node);}inline ll query(ll l,ll r,ll node) { if(l<=tree[node].l&&r>=tree[node].r) { return tree[node].sum; } spread(node);//标记下传 ll mid=(tree[node].l+tree[node].r)>>1,val=0; if(l<=mid) { val+=query(l,r,node<<1); } if(r>mid) { val+=query(l,r,(node<<1)|1); } return val;}inline void dfs(ll node,ll f,ll dep){ depth[node]=dep,fa[node]=f,size[node]=1;//更新节点深度,父节点,节点为根的子树大小 ll maxn=-1;//重儿子 for(register int i=last[node];i;i=ed[i].prev) { if(ed[i].to!=f)//遍历子节点 { dfs(ed[i].to,node,dep+1); size[node]+=size[ed[i].to];//更新子树大小 if(size[ed[i].to]>maxn) { heavy[node]=ed[i].to,maxn=size[ed[i].to];//标记重儿子 } } }}inline void ddfs(ll node,ll link){ id[node]=++ccnt,val[ccnt]=pre[node],top[node]=link;//标记节点新编号,赋初始值,更新节点所在重链的顶部 if(!heavy[node])//连重儿子都没有,肯定没有轻儿子,回溯 { return; } ddfs(heavy[node],link);//先遍历重儿子 for(register int i=last[node];i;i=ed[i].prev) { if(ed[i].to!=fa[node]&&ed[i].to!=heavy[node]) { ddfs(ed[i].to,ed[i].to);//遍历轻儿子,该节点所在重链顶部节点为自己 } }}//无脑操作inline ll queryPath(ll x,ll y)//查询路径点权和{ ll ans=0; while(top[x]!=top[y]) { if(depth[top[x]]<depth[top[y]]) { swap(x,y); } ans+=query(id[top[x]],id[x],1);//加上整个链的点权和 x=fa[top[x]];//爬到链顶 } if(depth[x]>depth[y]) { swap(x,y); } ans+=query(id[x],id[y],1); return ans;}inline ll querySubtree(ll root){ return query(id[root],id[root]+size[root]-1,1);}inline void changePath(ll x,ll y,ll val){ while(top[x]!=top[y]) { if(depth[top[x]]<depth[top[y]]) { swap(x,y); } add(id[top[x]],id[x],val,1); x=fa[top[x]]; } if(depth[x]>depth[y]) { swap(x,y); } add(id[x],id[y],val,1);}inline void changeSubtree(ll root,ll val){ add(id[root],id[root]+size[root]-1,val,1);}int main(){ nc=read(),cnt=read(); for(register int i=1;i<=nc;i++) { pre[i]=read(); } for(register int i=0;i<nc-1;i++) { from=read(),to=read(); addEdge(from,to),addEdge(to,from); } dfs(1,0,1),ddfs(1,1),create(1,nc,1); for(register int i=0;i<cnt;i++) { op=read(); switch(op) { case 1:{ x=read(),y=read(); changePath(x,x,y); break; } case 2:{ x=read(),y=read(); changeSubtree(x,y); break; } case 3:{ x=read(); cout<<queryPath(x,1)<<endl; break; } } }} 本文作者: Karry5307 本文链接: http://karry5307.github.io/2018/08/14/「HAOI2015」树上操作/ 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!