洛谷 P3178 [HAOI2015]树上操作
题目描述
有一棵点数为 N 的树,以点 1 为根,且树点有边权。然后有 M 个操作,分为三种:操作 1 :把某个节点 x 的点权增加 a 。操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。操作 3 :询问某个节点 x 到根的路径中所有点的点权和。
输入输出格式
输入格式:
第一行包含两个整数 N, M 。表示点数和操作数。接下来一行 N 个整数,表示树中节点的初始权值。接下来 N-1 行每行三个正整数 fr, to , 表示该树中存在一条边 (fr, to) 。再接下来 M 行,每行分别表示一次操作。其中第一个数表示该操作的种类( 1-3 ) ,之后接这个操作的参数( x 或者 x a ) 。
输出格式:
对于每个询问操作,输出该询问的答案。答案之间用换行隔开。
输入输出样例
输入样例#1:
5 5 1 2 3 4 5 1 2 1 4 2 3 2 5 3 3 1 2 1 3 5 2 1 2 3 3
输出样例#1:
6 9 13
说明
对于 100% 的数据, N,M<=100000 ,且所有输入数据的绝对值都不
会超过 10^6 。
树剖裸题
俩小时。。卧槽!
别用scanf读long long 的负数!
别用scanf读long long 的负数!
别用scanf读long long 的负数!
#include <iostream> #include <ctype.h> #include <cstdio> #define N 200005 #define M 100005 using namespace std; typedef long long LL; struct Tree { LL l,r,dis,lazy; }tr[N<<2|1]; struct node { LL next,to; }edge[N<<1]; LL dfn[M],dis[M],n,m,root,p,top[M],belong[M],tim,head[M],cnt,fa[M],dep[M],size[M]; void add(LL u,LL v) { edge[++cnt].next=head[u]; edge[cnt].to=v; head[u]=cnt; } void dfs1(LL now) { dep[now]=dep[fa[now]]+1; size[now]=1; for(LL i=head[now];i;i=edge[i].next) { LL v=edge[i].to; if(fa[now]!=v) { fa[v]=now; dfs1(v); size[now]+=size[v]; } } } void dfs2(LL now) { belong[now]=++tim; dfn[tim]=now; LL t=0; if(!top[now]) top[now]=now; for(LL i=head[now];i;i=edge[i].next) { LL v=edge[i].to; if(fa[now]!=v&&size[t]<size[v]) t=v; } if(t) top[t]=top[now],dfs2(t); for(LL i=head[now];i;i=edge[i].next) { LL v=edge[i].to; if(fa[now]!=v&&v!=t) dfs2(v); } } void up(LL k) {tr[k].dis=tr[k<<1].dis+tr[k<<1|1].dis ;} void build(LL k,LL l,LL r) { tr[k].l=l; tr[k].r=r; if(l==r) { tr[k].dis=dis[dfn[l]]; return; } LL mid=(l+r)>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r); up(k); } void swap(LL &a,LL &b) { LL tmp=b; b=a; a=tmp; } void pushdown(LL k) { if(tr[k].l==tr[k].r) return; tr[k<<1].lazy+=tr[k].lazy; tr[k<<1|1].lazy+=tr[k].lazy; tr[k<<1].dis=tr[k<<1].dis+(tr[k<<1].r-tr[k<<1].l+1)*tr[k].lazy; tr[k<<1|1].dis=tr[k<<1|1].dis+(tr[k<<1|1].r-tr[k<<1|1].l+1)*tr[k].lazy; tr[k].lazy=0; } void Tree_change(LL k,LL l,LL r,LL z) { if(tr[k].l==l&&tr[k].r==r) { tr[k].lazy+=z; tr[k].dis=tr[k].dis+(r-l+1)*z; return; } if(tr[k].lazy) pushdown(k); LL mid=(tr[k].l+tr[k].r)>>1; if(l>mid) Tree_change(k<<1|1,l,r,z); else if(r<=mid) Tree_change(k<<1,l,r,z); else Tree_change(k<<1,l,mid,z),Tree_change(k<<1|1,mid+1,r,z); up(k); } LL Tree_query(LL k,LL l,LL r) { if(tr[k].lazy) pushdown(k); if(tr[k].l==l&&tr[k].r==r) return tr[k].dis; LL mid=(tr[k].l+tr[k].r)>>1; if(l>mid) return Tree_query(k<<1|1,l,r); else if(r<=mid) return Tree_query(k<<1,l,r); else return (Tree_query(k<<1,l,mid)+Tree_query(k<<1|1,mid+1,r)); up(k); } void Chain_change(LL x,LL y,LL z) { for(;top[x]!=top[y];x=fa[top[x]]) { if(dep[top[x]]<dep[top[y]]) swap(x,y); Tree_change(1,belong[top[x]],belong[x],z); } if(dep[x]<dep[y]) swap(x,y); Tree_change(1,belong[y],belong[x],z); } LL Chain_query(LL x,LL y) { LL ans=0; for(;top[x]!=top[y];x=fa[top[x]]) { if(dep[top[x]]<dep[top[y]]) swap(x,y); ans=(ans+Tree_query(1,belong[top[x]],belong[x])); } if(dep[x]<dep[y]) swap(x,y); ans=(ans+Tree_query(1,belong[y],belong[x])); return ans; } void read(LL &x) { x=0;bool f=0; char ch=getchar(); while(!isdigit(ch)) {if(ch=='-') f=1;ch=getchar();} while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();} x=f?-x:x; } int main() { read(n);read(m); for(LL i=1;i<=n;i++) read(dis[i]); for(LL x,y,i=1;i<n;i++) { read(x);read(y); add(x,y); add(y,x); } dfs1(1); dfs2(1); build(1,1,n); for(LL opt,x,a;m--;) { read(opt); if(opt==1) { read(x);read(a); Tree_change(1,belong[x],belong[x],a); } else if(opt==2) { read(x);read(a); Tree_change(1,belong[x],belong[x]+size[x]-1,a); } else if(opt==3) { read(x); cout<<Chain_query(1,x)<<endl; } } return 0; }
我们都在命运之湖上荡舟划桨,波浪起伏着而我们无法逃脱孤航。但是假使我们迷失了方向,波浪将指引我们穿越另一天的曙光。