[BZOJ4034][HAOI2015]树上操作
[BZOJ4034][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 ) 。
输出
对于每个询问操作,输出该询问的答案。答案之间用换行隔开。
输入示例
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
输出示例
6 9 13
数据规模及约定
对于 100% 的数据, N,M<=100000 ,且所有输入数据的绝对值都不会超过 10^6 。
题解
树链剖分 + 线段树搞一搞就好啦。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <algorithm> using namespace std; int read() { int x = 0, f = 1; char c = getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); } return x * f; } #define maxn 100010 #define maxm 200010 #define LL long long int n, m, head[maxn], nxt[maxm], to[maxm]; LL val[maxn], Val[maxn]; void AddEdge(int a, int b) { to[++m] = b; nxt[m] = head[a]; head[a] = m; swap(a, b); to[++m] = b; nxt[m] = head[a]; head[a] = m; return ; } int fa[maxn], son[maxn], siz[maxn], top[maxn], clo, dl[maxn], dr[maxn]; void build(int u) { siz[u] = 1; for(int e = head[u]; e; e = nxt[e]) if(to[e] != fa[u]) { fa[to[e]] = u; build(to[e]); siz[u] += siz[to[e]]; if(!son[u] || siz[son[u]] < siz[to[e]]) son[u] = to[e]; } return ; } void gett(int u, int tp) { top[u] = tp; dl[u] = ++clo; if(son[u]) gett(son[u], tp); for(int e = head[u]; e; e = nxt[e]) if(to[e] != fa[u] && to[e] != son[u]) gett(to[e], to[e]); dr[u] = clo; return ; } LL addv[maxn<<2], sumv[maxn<<2]; void maintain(int o, int l, int r) { int lc = o << 1, rc = lc | 1; sumv[o] = sumv[lc] + sumv[rc] + addv[o] * (r - l + 1); return ; } void build(int o, int l, int r) { if(l == r) sumv[o] = Val[l]; else { int mid = l + r >> 1, lc = o << 1, rc = lc | 1; build(lc, l, mid); build(rc, mid + 1, r); maintain(o, l, r); } return ; } void pushdown(int o, int l, int r) { int mid = l + r >> 1, lc = o << 1, rc = lc | 1; if(l == r){ addv[o] = 0; return ; } addv[lc] += addv[o]; addv[rc] += addv[o]; sumv[lc] += addv[o] * (mid - l + 1); sumv[rc] += addv[o] * (r - mid); addv[o] = 0; return ; } void update(int o, int l, int r, int ql, int qr, int v) { pushdown(o, l, r); if(ql <= l && r <= qr) { addv[o] += v; sumv[o] += (LL)(r - l + 1) * v; return ; } int mid = l + r >> 1, lc = o << 1, rc = lc | 1; if(ql <= mid) update(lc, l, mid, ql, qr, v); if(qr > mid) update(rc, mid + 1, r, ql, qr, v); return maintain(o, l, r); } LL query(int o, int l, int r, int ql, int qr) { pushdown(o, l, r); if(ql <= l && r <= qr) return sumv[o]; int mid = l + r >> 1, lc = o << 1, rc = lc | 1; LL ans = 0; if(ql <= mid) ans += query(lc, l, mid, ql, qr); if(qr > mid) ans += query(rc, mid + 1, r, ql, qr); return ans; } LL ask(int u) { LL ans = 0; while(u) { int f = top[u]; ans += query(1, 1, n, dl[f], dl[u]); u = fa[f]; } return ans; } int main() { n = read(); int q = read(); for(int i = 1; i <= n; i++) val[i] = read(); for(int i = 1; i < n; i++) { int a = read(), b = read(); AddEdge(a, b); } build(1); gett(1, 1); for(int i = 1; i <= n; i++) Val[dl[i]] = val[i]; build(1, 1, n); while(q--) { int tp = read(), u = read(); if(tp == 1) { int v = read(); update(1, 1, n, dl[u], dl[u], v); } if(tp == 2) { int v = read(); update(1, 1, n, dl[u], dr[u], v); } if(tp == 3) printf("%lld\n", ask(u)); } return 0; }