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

浙公网安备 33010602011771号