bzoj 4034: [HAOI2015]树上操作 (树剖+线段树 子树操作)

4034: [HAOI2015]树上操作

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 6779  Solved: 2275
[Submit][Status][Discuss]

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

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

 

Sample Input

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

Sample Output

6
9
13

HINT

 

 对于 100% 的数据, N,M<=100000 ,且所有输入数据的绝对值都不会超过 10^6 。

 
思路:
在dfs的时候,加个mx数组,存下每个节点的子节点坐标在线段树中最大的值,那么对以这个点为根节点的子树进行操作是,也就是操作根节点到根节点坐标最大的子节点这段区间,然后就很容易了
实现代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define mid ll m = (l + r) >> 1
const ll M = 2e5+10;
struct node{
    ll to,next;
}e[M];
ll n,m,cnt1,cnt,a[M];
ll sum[M<<2],lazy[M<<2],son[M],fa[M],head[M],siz[M],top[M],dep[M],tid[M],rk[M],mx[M];
void add(ll u,ll v){
    e[++cnt1].to = v;e[cnt1].next = head[u];head[u] = cnt1;
    e[++cnt1].to = u;e[cnt1].next = head[v];head[v] = cnt1;
}

void dfs1(ll u,ll faz,ll deep){
     dep[u] = deep;
     fa[u] = faz;
     siz[u] = 1;
     for(ll i = head[u];i;i=e[i].next){
        ll v = e[i].to;
        if(v != fa[u]){
            dfs1(v,u,deep+1);
            siz[u] += siz[v];
            if(son[u] == -1||siz[v] > siz[son[u]])
                son[u] = v;
        }
     }
}

void dfs2(ll u,ll t){
    top[u] = t;
    mx[u] = cnt;
    tid[u] = cnt;
    rk[cnt] = u;
    cnt++;
    if(son[u] == -1) return;
    dfs2(son[u],t),mx[u] = max(mx[u],mx[son[u]]);
    for(ll i = head[u];i;i = e[i].next){
        ll v = e[i].to;
        if(v != son[u]&&v != fa[u])
            dfs2(v,v),mx[u]=max(mx[u],mx[v]);
    }
}


void pushup(ll rt){
    sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}

void pushdown(ll l,ll r,ll rt){
    if(lazy[rt]){
        mid;
        sum[rt<<1] += lazy[rt]*(m-l+1);
        sum[rt<<1|1] += lazy[rt]*(r - m);
        lazy[rt<<1] += lazy[rt];
        lazy[rt<<1|1] += lazy[rt];
        lazy[rt] = 0;
    }
}

void build(ll l,ll r,ll rt){
    lazy[rt] = 0;
    if(l == r){
        sum[rt] = a[rk[l]];
        return ;
    }
    mid;
    build(lson);
    build(rson);
    pushup(rt);
}

void update(ll L,ll R,ll c,ll l,ll r,ll rt){
    if(L <= l&&R >= r){
        sum[rt] += c*(r-l+1);
        lazy[rt]+=c;
        return ;
    }
    pushdown(l,r,rt);
    mid;
    if(L <= m) update(L,R,c,lson);
    if(R > m) update(L,R,c,rson);
    pushup(rt);
}

ll query(ll L,ll R,ll l,ll r,ll rt){
    if(L <= l&&R >= r){
        return sum[rt];
    }
    pushdown(l,r,rt);
    mid;
    ll ret = 0;
    if(L <= m) ret += query(L,R,lson);
    if(R > m) ret += query(L,R,rson);
    return ret;
}

ll ask(ll x,ll y){
    ll ans = 0;
    ll fx = top[x],fy = top[y];
    while(fx != fy){
        if(dep[fx] < dep[fy]) swap(x,y),swap(fx,fy);
        ans += query(tid[fx],tid[x],1,n,1);
        x = fa[fx];fx=top[x];
    }
    if(dep[x] > dep[y]) swap(x,y);
    ans += query(tid[x],tid[y],1,n,1);
    return ans;
}

int main()
{
    ll u,v,x,y,z;
    scanf("%lld%lld",&n,&m);
    cnt1 = 0;cnt = 1;
    memset(son,-1,sizeof(son));
    for(ll i = 1;i <= n;i ++){
        scanf("%lld",&a[i]);
    }
    for(ll i = 0;i < n-1;i++){
        scanf("%lld%lld",&u,&v);
        add(u,v);
    }
    dfs1(1,0,1);dfs2(1,1),build(1,n,1);
    while(m--){
        scanf("%lld%lld",&x,&y);
        if(x==1){
            scanf("%lld",&z);
            update(tid[y],tid[y],z,1,n,1);
        }
        else if(x == 2){
            scanf("%lld",&z);
            update(tid[y],mx[y],z,1,n,1);
        }
        else{
            printf("%lld\n",ask(1,y));
        }
    }
    return 0;
}

 

posted @ 2018-05-03 17:07  冥想选手  阅读(346)  评论(0编辑  收藏  举报