[bzoj4034][HAOI2015]T2
试题描述
有一棵点数为 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 <algorithm> #include <cmath> #include <stack> #include <vector> #include <queue> #include <cstring> #include <string> #include <map> #include <set> using namespace std; const int BufferSize = 1 << 16; char buffer[BufferSize], *Head, *tail; inline char Getchar() { if(Head == tail) { int l = fread(buffer, 1, BufferSize, stdin); tail = (Head = buffer) + l; } return *Head++; } 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, q, m, head[maxn], next[maxm], to[maxm], V[maxn]; void AddEdge(int a, int b) { to[++m] = b; next[m] = head[a]; head[a] = m; swap(a, b); to[++m] = b; next[m] = head[a]; head[a] = m; return ; } int siz[maxn], fa[maxn], son[maxn], w[maxn], ww, top[maxn], val[maxn]; void dfs(int u) { siz[u] = 1; for(int e = head[u]; e; e = next[e]) if(to[e] != fa[u]) { fa[to[e]] = u; dfs(to[e]); siz[u] += siz[to[e]]; if(siz[son[u]] < siz[to[e]]) son[u] = to[e]; } return ; } void build(int u, int tp) { w[u] = ++ww; top[u] = tp; if(son[u]) build(son[u], tp); for(int e = head[u]; e; e = next[e]) if(to[e] != fa[u] && to[e] != son[u]) build(to[e], to[e]); return ; } LL sumv[maxn<<2], addv[maxn<<2]; void build(int L, int R, int o) { if(L == R) sumv[o] = val[L]; else { int M = L + R >> 1, lc = o << 1, rc = lc | 1; build(L, M, lc); build(M+1, R, rc); sumv[o] = sumv[lc] + sumv[rc]; } return ; } int ql, qr; LL v; void update(int L, int R, int o) { if(ql <= L && R <= qr) addv[o] += v; else { int M = L + R >> 1, lc = o << 1, rc = lc | 1; if(ql <= M) update(L, M, lc); if(qr > M) update(M+1, R, rc); sumv[o] = sumv[lc] + addv[lc] * (M - L + 1); sumv[o] += sumv[rc] + addv[rc] * (R - M); } return ; } LL query(int L, int R, int o, LL Add) { Add += addv[o]; if(ql <= L && R <= qr) return sumv[o] + Add * (R - L + 1); int M = L + R >> 1, lc = o << 1, rc = lc | 1; LL ans = 0; if(ql <= M) ans += query(L, M, lc, Add); if(qr > M) ans += query(M+1, R, rc, Add); return ans; } LL query(int u) { int f = top[u]; LL ans = 0; while(u) { ql = w[f]; qr = w[u]; ans += query(1, n, 1, 0); u = fa[f]; f = top[u]; } return ans; } int main() { n = read(); q = read(); for(int i = 1; i <= n; i++) V[i] = read(); for(int i = 1; i < n; i++) AddEdge(read(), read()); dfs(1); build(1, 1); for(int i = 1; i <= n; i++) val[w[i]] = V[i]; build(1, n, 1); while(q--) { int tp = read(), u = read(); if(tp == 1) { v = read(); ql = qr = w[u]; update(1, n, 1); } else if(tp == 2) { v = read(); ql = w[u]; qr = ql + siz[u] - 1; update(1, n, 1); } else printf("%lld ", query(u)); } return 0; }