题目链接:P3178 [HAOI2015]树上操作
树链剖分模板题。。。不会戳我。
省选也出模板题,唉。。。
对于操作一,直接线段树单点修改即可。
对于操作二,根据同一子树节点编号连续的性质,直接区间修改即可。
对于操作三,就是查询1->x的答案(太模板了。。。)
#include <iostream> #include <cstdio> typedef long long ll; using namespace std; const ll N = 100010; struct seg_tree{ ll val, tag, l, r; }st[4 * N]; struct node{ ll pre, to; }edge[2 * N]; ll head[N], tot; ll n, m; ll w[N], sz[N], pos[N], len, bl[N], dep[N], fa[N], lst[N]; namespace chain{ void dfs1(ll x, ll f) { sz[x] = 1; for (ll i = head[x]; i; i = edge[i].pre) { ll y = edge[i].to; if (y == f) continue; dep[y] = dep[x] + 1; fa[y] = x; dfs1(y, x); sz[x] += sz[y]; } } void dfs2(ll x, ll chain) { ll k = 0; lst[x] = pos[x] = ++len; bl[x] = chain; for (ll i = head[x]; i; i = edge[i].pre) { ll y = edge[i].to; if (dep[y] < dep[x]) continue; if (sz[y] > sz[k]) { k = y; } } if (k) dfs2(k, chain); lst[x] = max(lst[x], lst[k]); for (ll i = head[x]; i; i = edge[i].pre) { ll y = edge[i].to; if (dep[y] < dep[x] || k == y) continue; dfs2(y, y); lst[x] = max(lst[x], lst[y]); } } }using namespace chain; namespace segment_tree{ void build(ll x, ll l, ll r) { st[x].l = l, st[x].r = r; if (l == r) return; ll mid = (l + r) >> 1; build(x << 1, l, mid); build(x << 1 | 1, mid + 1, r); } void update(ll x) { if (st[x].tag) { st[x << 1].tag += st[x].tag; st[x << 1 | 1].tag += st[x].tag; st[x << 1].val += (st[x << 1].r - st[x << 1].l + 1) * st[x].tag; st[x << 1 | 1].val += (st[x << 1 | 1].r - st[x << 1 | 1].l + 1) * st[x].tag; st[x].tag = 0; } } //单点修改 void change1(ll x, ll p, ll v) { ll l = st[x].l, r = st[x].r; if (l == r) { st[x].val += v; return; } ll mid = (l + r) >> 1; update(x); if (p <= mid) change1(x << 1, p, v); else change1(x << 1 | 1, p, v); st[x].val = st[x << 1].val + st[x << 1 | 1].val; } //区间修改 void change2(ll x, ll L, ll R, ll v) { ll l = st[x].l, r = st[x].r; if (r < L || l > R) return; if (L <= l && r <= R) { st[x].tag += v; st[x].val += (r - l + 1) * v; return; } update(x); change2(x << 1, L, R, v); change2(x << 1 | 1, L, R, v); st[x].val = st[x << 1].val + st[x << 1 | 1].val; } //区间求和 ll ask(ll x, ll L, ll R) { ll l = st[x].l, r = st[x].r; if (r < L || l > R) return 0; if (L <= l && r <= R) { return st[x].val; } update(x); return ask(x << 1, L, R) + ask(x << 1 | 1, L, R); } ll Qsum(ll x) { ll ret = 0; while (bl[x] != bl[1]) { ret += ask(1, pos[bl[x]], pos[x]); x = fa[bl[x]]; } ret += ask(1, pos[bl[x]], pos[x]); return ret; } }using namespace segment_tree; void add(ll u, ll v) { edge[++tot] = node{head[u], v}; head[u] = tot; } int main() { cin >> n >> m; for (ll i = 1; i <= n; i++) cin >> w[i]; for (ll i = 1, a, b; i < n; i++) { cin >> a >> b; add(a, b); add(b, a); } dfs1(1, 0); dfs2(1, 1); build(1, 1, len); for (ll i = 1; i <= n; i++) { change1(1, pos[i], w[i]); } while (m--) { ll opt, x, a; cin >> opt >> x; if (opt == 1) { cin >> a; change1(1, pos[x], a); } else if (opt == 2) { cin >> a; change2(1, pos[x], lst[x], a); } else { cout << Qsum(x) << "\n"; } } return 0; }