[HAOI2015]树上操作
题目描述
有一棵点数为 N 的树,以点 1 为根,且树点有边权。然后有 M 个操作,分为三种:操作 1 :把某个节点 x 的点权增加 a 。操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。操作 3 :询问某个节点 x 到根的路径中所有点的点权和。
输入输出格式
输入格式:
第一行包含两个整数 N, M 。表示点数和操作数。接下来一行 N 个整数,表示树中节点的初始权值。接下来 N-1 行每行两个正整数 from, to , 表示该树中存在一条边 (from, 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 。
solution
树链剖分裸题没什么好说的。。。
对于操作1,线段树单点修改
操作2,区间修改
操作3,相当于求节点1,到节点x这条链上的和。
然而求这么简单一道题,我还提交了8遍。。。
主要是细节:
1.把建树的1打成了l。
2.把查询中的这个
if(dep[tx]>dep[ty]) { swap(x,y); swap(tx,ty); }
写成了这样
if(dep[x]>dep[y]) { swap(x,y); swap(tx,ty); }
3.把一个+=写成了=
4.单点修改没有管懒标记。
代码
#include<bits/stdc++.h> using namespace std; const int MAXN = 100000 + 10; inline int read() { char ch; int fl=1; int xx=0; do{ ch= getchar(); if(ch=='-') fl=-1; }while(ch<'0'||ch>'9'); do{ xx=(xx<<3)+(xx<<1)+ch-'0'; ch=getchar(); }while(ch>='0'&&ch<='9'); return xx*fl; } int n,m; int a[MAXN]; struct node { int to; int next; };node g[MAXN*2]; int fa[MAXN],head[MAXN],dep[MAXN],tot[MAXN],son[MAXN],cnt=0; inline void addedge(int x,int y){ ++cnt;g[cnt].to=y;g[cnt].next=head[x];head[x]=cnt;return; } inline void dfs(int x) { tot[x]=1,son[x]=0; for(int i=head[x];i>0;i=g[i].next) { int v=g[i].to; if(v!=fa[x]) { dep[v]=dep[x]+1; fa[v]=x; dfs(v); if(tot[v]>tot[son[x]]) son[x]=v; tot[x]+=tot[v]; } } return; } int num[MAXN],top[MAXN],z=0; inline void dfs2(int now,int t) { top[now]=t;++z;num[now]=z; if(son[now]) dfs2(son[now],t); for(int i=head[now];i>0;i=g[i].next) { int v=g[i].to; if(v!=son[now]&&v!=fa[now]) dfs2(v,v); } } struct TREE { int l; int r; long long sum; inline int mid(){ return (l+r)>>1; } }tree[MAXN*4]; long long add[MAXN*4]; #define lc o<<1 #define rc o<<1|1 inline void build(int o,int l,int r) { tree[o].l=l;tree[o].r=r; int mid=tree[o].mid(); if(l==r) { tree[o].sum=0; return; } else { build(lc,l,mid); build(rc,mid+1,r); tree[o].sum=tree[lc].sum+tree[rc].sum; } } inline void pushup(int o) { add[lc]+=add[o]; add[rc]+=add[o]; tree[lc].sum+=(tree[lc].r-tree[lc].l+1)*add[o]; tree[rc].sum+=(tree[rc].r-tree[rc].l+1)*add[o]; add[o]=0; } inline void change(int o,int x,int y) { int l=tree[o].l,r=tree[o].r; int mid=tree[o].mid(); if(l==r) { tree[o].sum+=y; return; } else { pushup(o); if(x>=mid+1) change(rc,x,y); else change(lc,x,y); tree[o].sum=tree[lc].sum+tree[rc].sum; } } inline void change2(int o,int x,int y,long long z) { int l=tree[o].l,r=tree[o].r; if(x<=l&&y>=r) { add[o]+=z; tree[o].sum+=(tree[o].r-tree[o].l+1)*z; return; } else if(l>y||x>r) return ; else { if(add[o]) pushup(o); change2(lc,x,y,z); change2(rc,x,y,z); tree[o].sum=tree[lc].sum+tree[rc].sum; return; } } inline long long query(int o,int x,int y) { int l=tree[o].l,r=tree[o].r; if(x<=l&&y>=r) { return tree[o].sum; } else if(l>y||x>r) return 0; else { if(add[o]) pushup(o); return query(lc,x,y)+query(rc,x,y); } } inline long long query_sum(int x,int y) { int tx=top[x],ty=top[y]; long long ans=0; while(tx!=ty) { if(dep[tx]>dep[ty]) { swap(x,y); swap(tx,ty); } ans+=query(1,num[ty],num[y]); y=fa[ty];ty=top[y]; } if(dep[x]>dep[y]) { swap(x,y); } ans+=query(1,num[x],num[y]); return ans; } int main() { n=read();m=read(); for(int i=1;i<=n;i++) a[i]=read(),head[i]=-1; for(int i=1;i<n;i++) { int x,y; x=read(); y=read(); addedge(x,y); addedge(y,x); } fa[1]=0;dep[1]=1;dfs(1); dfs2(1,1); build(1,1,z); for(int i=1;i<=n;i++) change(1,num[i],a[i]); for(int i=1;i<=m;i++) { long long op,x,y; op=read(); if(op==1) { x=read(),y=read(); change(1,num[x],y); } if(op==2) { x=read();y=read(); change2(1,num[x],num[x]+tot[x]-1,y); } if(op==3) { x=read(); printf("%lld\n",query_sum(1,x)); } } return 0; }