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); } } }