HAOI2015 树上操作

题目描述

有一棵点数为 N 的树,以点 1 为根,且树点有边权。然后有 M 个操作,分为三种:操作 1 :把某个节点 x 的点权增加 a 。操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。操作 3 :询问某个节点 x 到根的路径中所有点的点权和。

输入输出格式

输入格式:

第一行包含两个整数 N, M 。表示点数和操作数。接下来一行 N 个整数,表示树中节点的初始权值。接下来 N-1 行每行三个正整数 fr, to , 表示该树中存在一条边 (fr, 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 。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cassert>

#define DEBUG printf("passing [%s] in line %d\n", __FUNCTION__, __LINE__)

using namespace std;

typedef long long ll;

const int N = 500010;

int n, m, head[2 * N], rest[2 * N], to[2 * N], tot;

ll val[N];

void add(int u, int v) {
    ++ tot;
    to[tot] = v;
    rest[tot] = head[u];
    head[u] = tot;
}

namespace segTree {
    ll tag[N * 4], sum[N * 4];
    int l[N * 4], r[N * 4];
    void push(int id) {
        if(id[tag]) {
            (id << 1)[tag] += id[tag];
            (id << 1 | 1)[tag] += id[tag];
            id[sum] += id[tag] * (id[r] - id[l] + 1);
            id[tag] = 0;
        }
    }
    void update(int id) {
        push(id);
        push(id << 1);
        push(id << 1 | 1);
        id[sum] = (id << 1)[sum] + (id << 1 | 1)[sum];
    }
    void build(int id, int cl, int cr) {
        int cm = (cl + cr) >> 1;
        id[l] = cl;
        id[r] = cr;
        if(cl == cr) return;
        build(id << 1, cl, cm);
        build(id << 1 | 1, cm + 1, cr);
    }
    void modify(int id, int ql, int qr, ll val) {
        push(id);
        int ll = id[l], rr = id[r];
        if(ll > qr || rr < ql) return;
        if(ql <= ll && rr <= qr) {
            id[tag] += val;
        } else {
            modify(id << 1, ql, qr, val);
            modify(id << 1 | 1, ql, qr, val);
            update(id);
        }
    }
    ll query(int id, int ql, int qr) {
        push(id);
        int ll = id[l], rr = id[r];
        if(ll > qr || rr < ql) return 0;
        if(ql <= ll && rr <= qr) {
            return sum[id];
        } else {
            return query(id << 1, ql, qr) + query(id << 1 | 1, ql, qr);
        }
    }
}

namespace splitTree {
    int l[N], r[N], fa[N], sz[N], son[N], top[N], cnt;
    void dfs1(int u) {
        u[sz] = 1;
        for(int i = head[u] ; i ; i = rest[i]) {
            int v = to[i];
            if(v != u[fa]) {
                v[fa] = u;
                dfs1(v);
                u[sz] += v[sz];
                if(u[son][sz] < v[sz]) {
                    u[son] = v;
                }
            }
        }
    }
    void dfs2(int u, int tp) {
        u[top] = tp;
        u[l] = ++ cnt;
        if(u[son]) dfs2(u[son], tp);
        for(int i = head[u] ; i ; i = rest[i]) {
            int v = to[i];
            if(v != u[fa] && v != u[son]) {
                dfs2(v, v);
            }
        }
        u[r] = cnt;
    }

    void work() {
        dfs1(1);
        dfs2(1, 1);
        segTree :: build(1, 1, n);
        for(int i = 1 ; i <= n ; ++ i) {
            segTree :: modify(1, i[l], i[l], i[val]);
        }
    }
}

namespace solve {
    void addPoint(int u, ll a) {
        segTree :: modify(1, u[splitTree :: l], u[splitTree :: l], a);
    }
    void addTree(int u, ll a) {
        segTree :: modify(1, u[splitTree :: l], u[splitTree :: r], a);
    }
    void ask(int u) {
        ll ans = 0;
        while(u) {
            ans += segTree :: query(1, u[splitTree :: top][splitTree :: l], u[splitTree :: l]);
            u = u[splitTree :: top][splitTree :: fa];
        }
        printf("%lld\n", ans);
    }
}

int main() {
    scanf("%d%d", &n, &m);
    for(int i = 1 ; i <= n ; ++ i) {
        scanf("%lld", &val[i]);
    }
    for(int i = 1, u, v ; i < n ; ++ i) {
        scanf("%d%d", &u, &v);
        add(u, v);
        add(v, u);
    }
    splitTree :: work();
    for(int i = 1, op, x ; i <= m ; ++ i) {
        scanf("%d", &op);
        if(op == 1) {
            ll a;
            scanf("%d%lld", &x, &a);
            solve :: addPoint(x, a);
        } else if(op == 2) {
            ll a;
            scanf("%d%lld", &x, &a);
            solve:: addTree(x, a);
        } else {
            scanf("%d", &x);
            solve :: ask(x);
        }
    }
}

  

转载于:https://www.cnblogs.com/KingSann/articles/7636079.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值