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

 

posted @ 2017-03-20 11:01  xjr01  阅读(177)  评论(0编辑  收藏  举报