【HAOI2015】树上操作

随声附和 提交于 2020-01-22 22:02:02

(题面来自洛谷)

题目描述

有一棵点数为 N 的树,以点 1 为根,且树点有边权。然后有 M 个操作,分为三种:

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

数据范围:N <= 1e5


分析:简化版的ETT。建立括号序列,操作1、3都很容易解决。为了实现操作2,给序列上的点打标记,表示这里存储的是原节点权值的正/负值。用线段树维护区间和,上推时分别合并正负节点数,区间修改时每个线段树节点值\(sum+=val*(pos-neg)\),这样就实现了正负节点的区分操作。同时要求移植子树就变成了ETT的模板题,用Splay/FHQ维护即可。
代码:

#include <iostream>
#include <cstdio>
#include <cstring>
const int maxn(100010);
typedef long long LL;
using namespace std;
int n, m;
LL wt[maxn];
int head[maxn], etop;
struct E {
    int to, nxt;
} edge[maxn<<1];
inline void insert(int u, int v) {
    edge[++etop] = (E) {v, head[u]};
    head[u] = etop;
}
int fst[maxn], sec[maxn], tmr;
LL dat[maxn<<1];
bool cat[maxn<<1];
void dfs(int u, int pre) {
    fst[u] = ++tmr;
    dat[tmr] = wt[u];
    cat[tmr] = 1;
    for (int i = head[u], v; i; i = edge[i].nxt) {
        if ((v = edge[i].to) == pre) continue;
        dfs(v, u);
    }
    sec[u] = ++tmr;
    dat[tmr] = -wt[u];
    cat[tmr] = 0;
    return;
}
namespace Seg_tree {
    #define lc (nd<<1)
    #define rc ((nd<<1)|1)
    #define mid ((l + r) >> 1)
    struct node {
        LL sum;
        int pos, neg;
        friend node operator + (node a, node b) {
            return (node) {a.sum + b.sum, a.pos + b.pos, a.neg + b.neg};
        }
        friend node operator * (node a, LL b) {
            return (node) {a.sum + b * (a.pos-a.neg), a.pos, a.neg};
        }
    } seg[maxn<<3];
    LL tag[maxn<<3];
    inline void update(int nd) {
        seg[nd] = seg[lc] + seg[rc];
    }
    inline void put_tag(int nd, LL val) {
        seg[nd] = seg[nd] * val;
        tag[nd] += val;
    }
    inline void push_down(int nd) {
        put_tag(lc, tag[nd]);
        put_tag(rc, tag[nd]);
        tag[nd] = 0;
    }
    void build(int nd, int l, int r) {
        if (l == r) {
            seg[nd] = (node) {dat[l], cat[l], !cat[l]};
            return;
        }
        build(lc, l, mid);
        build(rc, mid+1, r);
        update(nd);
    }
    void add(int nd, int l, int r, int ql, int qr, LL val) {
        if (l >= ql && r <= qr) {
            put_tag(nd, val);
            return;
        }
        if (r < ql || l > qr) return;
        push_down(nd);
        add(lc, l, mid, ql, qr, val);
        add(rc, mid+1, r, ql, qr, val);
        update(nd);
    }
    LL query(int nd, int l, int r, int ql, int qr) {
        if (l >= ql && r <= qr) {
            return seg[nd].sum;
        }
        if (r < ql || l > qr) return 0;
        push_down(nd);
        return query(lc, l, mid, ql, qr) + query(rc, mid+1, r, ql, qr);
    }
} using namespace Seg_tree;
int main() {
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; ++i) scanf("%lld", &wt[i]);
    int u, v;
    for (int i = 1; i < n; ++i) {
        scanf("%d %d", &u, &v);
        insert(u, v), insert(v, u);
    }
    dfs(1, 0);
    build(1, 1, 2*n);
    int opt;
    while (m--) {
        scanf("%d %d", &opt, &u);
        if (opt == 3) {
            printf("%lld\n", query(1, 1, 2*n, 1, fst[u]));
            continue;
        }
        scanf("%d", &v);
        if (opt == 1) {
            add(1, 1, 2*n, fst[u], fst[u], v);
            add(1, 1, 2*n, sec[u], sec[u], v);
        } else add(1, 1, 2*n, fst[u], sec[u], v);
    }
    return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!