bzoj4034

Description

 有一棵点数为 N 的树,以点 1 为根,且树点有边权。然后有 M 个

操作,分为三种:
操作 1 :把某个节点 x 的点权增加 a 。
操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。
操作 3 :询问某个节点 x 到根的路径中所有点的点权和。

Input

 第一行包含两个整数 N, M 。表示点数和操作数。

接下来一行 N 个整数,表示树中节点的初始权值。
接下来 N-1 行每行三个正整数 fr, to , 表示该树中存在一条边 (fr, to) 。
再接下来 M 行,每行分别表示一次操作。其中第一个数表示该操
作的种类( 1-3 ) ,之后接这个操作的参数( x 或者 x a ) 。

Output

 对于每个询问操作,输出该询问的答案。答案之间用换行隔开。

预处理树的前序和后序遍历的编号

按编号用两棵线段树分别维护区间和

子树的编号连续可以直接修改

结点到根的路径和可以用前序和后序的信息相减得到

时间复杂度O(nlogn)

#include<cstdio>
#include<cstring>
#define N 100005
typedef long long lint;
int n,m,a,b,o;
int v[N];
int es[N*3],enx[N*3],ep=N;
void addedge(int a,int b){
    es[ep]=b;
    enx[ep]=enx[a];
    enx[a]=ep++;
    es[ep]=a;
    enx[ep]=enx[b];
    enx[b]=ep++;
}
struct sgt{
    lint s[262144];
    lint a[262144];
    sgt(){
        memset(s,0,sizeof(s));
        memset(a,0,sizeof(a));
    }
    void down(int w,int l,int r){
        if(l<r){
            a[w+w]+=a[w];
            a[w+w+1]+=a[w];
            int x=(r+1-l)>>1;
            s[w+w]+=a[w]*x;
            s[w+w+1]+=a[w]*x;
        }
        a[w]=0;
    }
    void add(int l,int r,lint x,int w=1,int L=1,int R=131072){
        int M=L+R>>1;
        if(a[w])down(w,L,R);
        if(l==L&&r==R){
            a[w]+=x;
            s[w]+=x*(R+1-L);
            return;
        }
        if(r<=M)add(l,r,x,w+w,L,M);
        else if(l>M)add(l,r,x,w+w+1,M+1,R);
        else add(l,M,x,w+w,L,M),add(M+1,r,x,w+w+1,M+1,R);
        s[w]=s[w+w]+s[w+w+1];
    }
    lint get(int l,int r,int w=1,int L=1,int R=131072){
        int M=L+R>>1;
        if(a[w])down(w,L,R);
        if(l==L&&r==R)return s[w];
        if(r<=M)return get(l,r,w+w,L,M);
        else if(l>M)return get(l,r,w+w+1,M+1,R);
        else return get(l,M,w+w,L,M)+get(M+1,r,w+w+1,M+1,R);
    }
}t1,t2;
int i1[N],i2[N],m1[N],m2[N],p1=1,p2=2;
void f(int w,int pa){
    i1[w]=p1++;
    m2[w]=p2;
    for(int i=enx[w];i;i=enx[i]){
        int u=es[i];
        if(u!=pa)f(u,w);
    }
    m1[w]=p1-1;
    i2[w]=p2++;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",v+i);
    for(int i=1;i<n;i++){
        scanf("%d%d",&a,&b);
        addedge(a,b);
    }
    f(1,0);
    for(int i=1;i<=n;i++){
        t1.add(i1[i],i1[i],v[i]);
        t2.add(i2[i],i2[i],v[i]);
    }
    while(m--){
        scanf("%d",&o);
        if(o==1){
            scanf("%d%d",&a,&b);
            t1.add(i1[a],i1[a],b);
            t2.add(i2[a],i2[a],b);
        }else if(o==2){
            scanf("%d%d",&a,&b);
            t1.add(i1[a],m1[a],b);
            t2.add(m2[a],i2[a],b);
        }else{
            scanf("%d",&a);
            printf("%lld
",t1.get(1,i1[a])-t2.get(1,m2[a]-1));
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/ccz181078/p/5171235.html