2568. 树链剖分(树链剖分,@模板题)

活动 - AcWing

给定一棵树,树中包含 n 个节点(编号 1∼n),其中第 ii 个节点的权值为 ai。

初始时,1 号节点为树的根节点。

现在要对该树进行 m 次操作,操作分为以下 4 种类型:

  • 1 u v k,修改路径上节点权值,将节点 u 和节点 v 之间路径上的所有节点(包括这两个节点)的权值增加 kk。
  • 2 u k,修改子树上节点权值,将以节点 u 为根的子树上的所有节点的权值增加 k。
  • 3 u v,询问路径,询问节点 u 和节点 v 之间路径上的所有节点(包括这两个节点)的权值和。
  • 4 u,询问子树,询问以节点 u 为根的子树上的所有节点的权值和。
输入格式

第一行包含一个整数 n,表示节点个数。

第二行包含 n 个整数,其中第 i 个整数表示 ai。

接下来 n−1 行,每行包含两个整数 x,y,表示节点 x 和节点 y 之间存在一条边。

再一行包含一个整数 m,表示操作次数。

接下来 m 行,每行包含一个操作,格式如题目所述。

输出格式

对于每个操作 3 和操作 4,输出一行一个整数表示答案。

数据范围

1≤n,m≤105
0≤ai,k≤105
1≤u,v,x,y≤n

输入样例:
5
1 3 7 4 5
1 3
1 4
1 5
2 3
5
1 3 4 3
3 5 4
1 3 5 10
2 3 5
4 1
输出样例:
16
69

解析:

#include <iostream>
#include <string>
#include <cstring>
#include <cmath>
#include <ctime>
#include <algorithm>
#include <utility>
#include <stack>
#include <queue>
#include <vector>
#include <set>
#include <math.h>
#include <map>
#include <sstream>
#include <deque>
#include <unordered_map>
#include <unordered_set>
#include <bitset>
#include <stdio.h>
#include <tuple>
using namespace std;
/*
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
*/
typedef long long LL;
#define int long long
#define ld long double
//#define INT __int128
const LL INF = 0x3f3f3f3f3f3f3f3f;
typedef unsigned long long ULL;
typedef pair<long long, long long> PLL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
const int inf = 0x3f3f3f3f;
const LL mod = 998244353;
const ld eps = 1e-12;
const int N = 1e5 + 10, M = 2 * N + 10;
int n, Q;
int w[N], nw[N], id[N], fa[N], tot;
int dep[N], son[N], top[N], siz[N];
int h[N], e[M], ne[M], idx;
struct TREE {
	int l, r;
	int fg, sum;
}tr[N << 2];
void add(int a, int b) {
	e[idx] = b, ne[idx] = h[a], h[a] = idx++;
	e[idx] = a, ne[idx] = h[b], h[b] = idx++;
}
void dfs1(int u, int p, int d) {
	siz[u] = 1, fa[u] = p, dep[u] = d;
	int mx = 0;
	for (int i = h[u]; i != -1; i = ne[i]) {
		int j = e[i];
		if (j == p)continue;
		dfs1(j, u, d + 1);
		siz[u] += siz[j];
		if (siz[son[u]] < siz[j])son[u] = j;
	}
}
void dfs2(int u, int link) {
	id[u] = ++tot, nw[tot] = w[u], top[u] = link;
	if (son[u])dfs2(son[u], link);
	for (int i = h[u]; i != -1; i = ne[i]) {
		int j = e[i];
		if (j == fa[u] || j == son[u])continue;
		dfs2(j, j);
	}
}
#define ls u<<1
#define rs u<<1|1
void up(int u) {
	tr[u].sum = tr[ls].sum + tr[rs].sum;
}
void down(int u) {
	if (tr[u].fg) {
		tr[ls].fg += tr[u].fg;
		tr[ls].sum += tr[u].fg * (tr[ls].r - tr[ls].l + 1);
		tr[rs].fg += tr[u].fg;
		tr[rs].sum += tr[u].fg * (tr[rs].r - tr[rs].l + 1);
		tr[u].fg = 0;
	}
}
void build(int u, int l, int r) {
	tr[u].l = l, tr[u].r = r;
	if (l == r) {
		tr[u].sum = nw[l];
		return;
	}
	int mid = l + r >> 1;
	build(ls, l, mid), build(rs, mid + 1, r);
	up(u);
}
void modify(int u, int l, int r, int k) {
	if (l <= tr[u].l && tr[u].r <= r) {
		tr[u].sum += k * (tr[u].r - tr[u].l + 1);
		tr[u].fg += k;
		return;
	}
	down(u);
	int mid = tr[u].l + tr[u].r >> 1;
	if (l <= mid)modify(ls, l, r, k);
	if (r > mid)modify(rs,l, r, k);
	up(u);
}
int query(int u, int l, int r) {
	if (l <= tr[u].l && tr[u].r <= r) {
		return tr[u].sum;
	}
	down(u);
	int mid = tr[u].l + tr[u].r >> 1;
	int ret = 0;
	if (l <= mid)ret += query(ls, l, r);
	if (r > mid)ret += query(rs, l, r);
	up(u);
	return ret;
}
void path(int u, int v, int k) {
	while (top[u] != top[v]) {
		if (dep[top[u]] < dep[top[v]])swap(u, v);
		modify(1, id[top[u]], id[u], k);
		//cout << "------------" << u << " " << v << " " << top[u] << " " << top[v] << endl;
		//cout << "++++++++++++++++++++  " << u << " " << top[u] << " " << id[u] << " " << id[top[u]] << endl;
		u = fa[top[u]];
	}
	if (dep[u] < dep[v])swap(u, v);
	//cout << "++++++++++++" << u << " " << v << " " << id[u] << " " << id[v] << endl;
	modify(1, id[v], id[u], k);
}
int ask(int u, int v) {
	int ret = 0;
	while (top[u] != top[v]) {
		if (dep[top[u]] < dep[top[v]])swap(u, v);
		ret += query(1, id[top[u]], id[u]);
		//cout << "====================  " << u << " " << top[u] << " " << id[u] << " " << id[top[u]] << endl;
		u = fa[top[u]];
	}
	if (dep[u] < dep[v])swap(u, v);
	ret += query(1, id[v], id[u]);
	//cout << "================" << u << " " << v << " " << id[u] << " " << id[v] << endl;
	return ret;
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	cin >> n;
	memset(h, -1, sizeof h);
	for (int i = 1; i <= n; i++) {
		cin >> w[i];
	}
	for (int i = 1; i < n; i++) {
		int a, b;
		cin >> a >> b;
		add(a, b);
	}
	dfs1(1,0,1);
	dfs2(1, 1);
	build(1, 1, n);
	/*cout << "_____________________________" << endl;
	for (int i = 1; i <= n; i++) {
		cout << id[i] << " ";
	}
	cout << endl;
	cout << endl;*/
	cin >> Q;
	int op;
	while (Q--) {
		cin >> op;
		if (op == 1) {
			int u, v, k;
			cin >> u >> v >> k;
			path(u, v, k);
		}
		else if (op == 2) {
			int u, k;
			cin >> u >> k;
			modify(1, id[u], id[u] + siz[u]-1,k);
		}
		else if (op == 3) {
			int u, v;
			cin >> u >> v;
			cout << ask(u, v) << endl;
		}
		else {
			int u;
			cin >> u;
			//cout << "----------------" << id[u] << " " << id[u] + siz[u]-1 << endl;
			cout << query(1, id[u], id[u] + siz[u]-1) << endl;
		}
	}
	return 0;
}

/*
5
1 3 7 4 5
1 3
1 4
1 5
2 3
6
3 5 4
1 3 4 3
3 1 1
3 4 4 
3 5 5
3 5 4


5
1 3 7 4 5
1 3
1 4
1 5
2 3
10
1 3 4 3
3 5 4
1 3 5 10
3 3 3
3 1 1 
3 5 5
3 3 5
2 3 5
3 2 2
4 1
*/

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值