【传送门:BZOJ4034】
简要题意:
给出一棵有n个有权节点的树且根节点为1,有m个操作,3种操作:
1 x a将x点的权值增加a
2 x a将x的子树的所有节点增加a
3 x求出x到根节点的路径上的所有点的权值和
题解:
裸树链剖分,只要在处理轻重链的时候记录x子树的最小的编号和最大的编号即可
注意加long long
参考代码:
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<cstdlib> using namespace std; typedef long long LL; struct node { int x,y,next; }a[210000];int len,last[110000]; void ins(int x,int y) { len++; a[len].x=x;a[len].y=y; a[len].next=last[x];last[x]=len; } int tot[110000],dep[110000],fa[110000],son[110000]; void pre_tree_node(int x) { tot[x]=1;son[x]=0; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(y!=fa[x]) { fa[y]=x; dep[y]=dep[x]+1; pre_tree_node(y); tot[x]+=tot[y]; if(tot[y]>tot[son[x]]) son[x]=y; } } } int top[110000],ys[110000],z,L[110000],R[110000]; void pre_tree_edge(int x,int tp) { ys[x]=++z;top[x]=tp; L[x]=z; if(son[x]!=0) pre_tree_edge(son[x],tp); for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(y!=fa[x]&&y!=son[x]) pre_tree_edge(y,y); } R[x]=z; } struct trnode { int l,r,lc,rc;LL c,lazy; }tr[210000];int trlen; LL s[110000]; void bt(int l,int r) { trlen++;int now=trlen; tr[now].l=l;tr[now].r=r;tr[now].c=tr[now].lazy=0; tr[now].lc=tr[now].rc=-1; if(l<r) { int mid=(l+r)/2; tr[now].lc=trlen+1;bt(l,mid); tr[now].rc=trlen+1;bt(mid+1,r); } } void update(int now) { int lc=tr[now].lc,rc=tr[now].rc; if(lc!=-1) tr[lc].c+=LL(tr[lc].r-tr[lc].l+1)*tr[now].lazy,tr[lc].lazy+=tr[now].lazy; if(rc!=-1) tr[rc].c+=LL(tr[rc].r-tr[rc].l+1)*tr[now].lazy,tr[rc].lazy+=tr[now].lazy; tr[now].lazy=0; } void change(int now,int l,int r,int c) { if(tr[now].l==l&&tr[now].r==r) { tr[now].c+=LL(r-l+1)*c; tr[now].lazy+=c; return ; } int lc=tr[now].lc,rc=tr[now].rc,mid=(tr[now].l+tr[now].r)/2; if(tr[now].lazy!=0) update(now); if(r<=mid) change(lc,l,r,c); else if(l>mid) change(rc,l,r,c); else change(lc,l,mid,c),change(rc,mid+1,r,c); tr[now].c=tr[lc].c+tr[rc].c; } LL getsum(int now,int l,int r) { if(tr[now].l==l&&tr[now].r==r) return tr[now].c; int lc=tr[now].lc,rc=tr[now].rc,mid=(tr[now].l+tr[now].r)/2; if(tr[now].lazy!=0) update(now); if(r<=mid) return getsum(lc,l,r); else if(l>mid) return getsum(rc,l,r); else return getsum(lc,l,mid)+getsum(rc,mid+1,r); } LL solve(int x) { int tx=top[x];LL ans=0; while(tx!=1) { ans+=getsum(1,ys[tx],ys[x]); x=fa[tx];tx=top[x]; } return ans+getsum(1,ys[tx],ys[x]); } int main() { int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%lld",&s[i]); len=0;memset(last,0,sizeof(last)); for(int i=1;i<n;i++) { int x,y; scanf("%d%d",&x,&y); ins(x,y);ins(y,x); } fa[1]=0;dep[1]=0;pre_tree_node(1); z=0;pre_tree_edge(1,1); trlen=0;bt(1,z); for(int i=1;i<=n;i++) change(1,ys[i],ys[i],s[i]); for(int i=1;i<=m;i++) { int opt,x;LL a; scanf("%d%d",&opt,&x); if(opt==1) { scanf("%lld",&a); change(1,ys[x],ys[x],a); } if(opt==2) { scanf("%lld",&a); change(1,L[x],R[x],a); } if(opt==3) printf("%lld ",solve(x)); } return 0; }