BZOJ 4034 树上操作(树链剖分 + 线段树)

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4034

题目大意:有一棵以1为根节点的树,每个节点上都有权值,接下来有m次操作,每次操作有三种操作:

1 x a :令x节点的权值增加a;

2 x a :令以x为根节点的子树上所有节点的权值都增加a;

3 x :查询从x到根节点1的所有节点的权值和。

题目思路:刚刚开始学树链剖分,看了看一些博客对树链剖分也有一些了解了。这题是一个树链剖分的裸题,根据dfs序的性质,子树的节点的dfs序号是连续的,这样可以很简单的用线段来维护第一和第二种操作了,第三个查询就用到了树链剖分的一些性质,查询x到根节点1的所有权值和时可以把根节点1到x的这一条链分解成x到top[x](top[x]就是x所在链的端点),fa[top[x]]到top[fa[top[x]]],...,直到遍历到根节点1为止,然后用线段树的区间查询将这几部分的和加起来就行了,具体操作看代码吧。(ps:这题蛮不错的,很适合树链剖分的新学者,可以更加清楚的理解树链剖分)。

#include <bits/stdc++.h>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
typedef long long LL;
const int MX = 1e5+7;

int n,m;
int a[MX];
int in[MX],out[MX],tot;
int top[MX],sz[MX],son[MX],dep[MX],fa[MX];
LL sum[MX<<2],lazy[MX<<2];
vector<int>E[MX];

void init(){
    memset(son,0,sizeof(son));
    memset(fa,0,sizeof(fa));
    for(int i = 0;i <= n;i++) E[i].clear();
    tot = 0;
}

void add_edge(int u,int v){
    E[u].push_back(v);
    E[v].push_back(u);
}

void dfs1(int u){
    sz[u] = 1;
    for(int i = 0;i < E[u].size();i++){
        int v = E[u][i];
        if(v == fa[u]) continue;
        fa[v] = u;dep[v] = dep[u] + 1;
        dfs1(v);
        sz[u] += sz[v];
        if(sz[v] > sz[son[u]]) son[u] = v;
     }
}

void dfs2(int u,int tp){
    in[u] = ++tot;top[u] = tp;
    if(son[u]) dfs2(son[u],tp);
    for(int i = 0;i < E[u].size();i++){
        int v = E[u][i];
        if(v == fa[u] || v == son[u]) continue;
        dfs2(v,v);
    }
    out[u] = tot;
}

void push_up(int rt){
    sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}

void push_down(int rt,int m){
    if(lazy[rt]){
        lazy[rt<<1] += lazy[rt];
        lazy[rt<<1|1] += lazy[rt];
        sum[rt<<1] += lazy[rt]*(m-(m>>1));
        sum[rt<<1|1] += lazy[rt]*(m>>1);
        lazy[rt] = 0;
    }
}

void build(int l,int r,int rt){
    sum[rt] = lazy[rt] = 0;
    if(l == r) return;
    int m = (l+r)>>1;
    build(lson);
    build(rson);
    push_up(rt);
}

void update(int L,int R,LL d,int l,int r,int rt){
    if(L <= l && r <= R){
        sum[rt] += (LL)d*(r-l+1);
        lazy[rt] += d;
        return;
    }
    push_down(rt,r-l+1);
    int m = (l+r)>>1;
    if(L <= m) update(L,R,d,lson);
    if(R > m) update(L,R,d,rson);
    push_up(rt);
}

LL query(int L,int R,int l,int r,int rt){
    if(L <= l && r <= R) return sum[rt];
    push_down(rt,r-l+1);
    int m = (l + r) >> 1;
    LL res = 0;
    if(L <= m) res += query(L,R,lson);
    if(R > m) res += query(L,R,rson);
    return res;
}

LL solve(int x){
    LL res = 0;
    while(top[x] != 1){
        res += query(in[top[x]],in[x],1,n,1);
        x = fa[top[x]];
    }
    res += query(1,in[x],1,n,1);
    return res;
}

int main(){
    while(~scanf("%d%d",&n,&m)){
        init();
        build(1,n,1);
        for(int i = 1;i <= n;i++) scanf("%d",&a[i]);
        for(int i = 1;i < n;i++){
            int u,v;scanf("%d%d",&u,&v);
            add_edge(u,v);
        }
        dfs1(1);dfs2(1,1);
        for(int i = 1;i <= n;i++)
            update(in[i],in[i],a[i],1,n,1);
        while(m--){
            int op;scanf("%d",&op);
            if(op == 1){
                int x,d;scanf("%d%d",&x,&d);
                update(in[x],in[x],d,1,n,1);
            } else if(op == 2){
                int x,d;scanf("%d%d",&x,&d);
                update(in[x],out[x],d,1,n,1);
            } else{
                int x;scanf("%d",&x);
                printf("%lld\n",solve(x));
            }
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值