(题面来自洛谷)
题目描述
有一棵点数为 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; }
来源:https://www.cnblogs.com/TY02/p/12229687.html