P3384 【模板】轻重链剖分/树链剖分

原题链接
注意:

  • 以新审旧.
  • 线段树部分不要出错.
  • 点编号向线段树下标的映射.
    代码如下:
#include <cstdio>
#include <cctype>
#include <cstring>
#include <cmath>
#include <iostream>

using namespace std; 

#define ll long long

inline ll read() {
	ll x = 0, f = 0; char ch = getchar(); 
	while (!isdigit(ch)) f = ch == '-', ch = getchar(); 
	while (isdigit(ch)) x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar(); 
	return f ? -x : x; 
}

void print(int x) {
	if (x < 0) x = -x, putchar('-');
	if (x < 10) putchar(x + '0'); 
	else {
		print(x / 10); 
		putchar(x % 10 + '0'); 
	}
}

const int N = 1e5 + 5; 
int n, m, rt, vt[N]; ll p; 
int head[N], nex[N << 1], ver[N << 1], tot; 
int siz[N], dep[N], fa[N], ms[N];
int f1[N], f2[N], top[N], num; 

inline void AddMod(ll &x, ll y) { x = (x + y) % p; }

void Addedge(int x, int y) {
	ver[++tot] = y; 
	nex[tot] = head[x]; 
	head[x] = tot; 
}

void dfs1(int x, int fat) {
	fa[x] = fat; siz[x] = 1; 
	dep[x] = dep[fat] + 1; 
	for (int i = head[x]; i; i = nex[i]) {
		int y = ver[i]; 
		if (y == fat) continue; 
		dfs1(y, x); 
		siz[x] += siz[y]; 
		if (siz[y] > siz[ms[x]]) ms[x] = y; 
	}
}

void dfs2(int x, int topx) {
	top[x] = topx; 
	f1[x] = ++num; f2[num] = x; 
	if (!ms[x]) return; 
	dfs2(ms[x], topx); 
	for (int i = head[x]; i; i = nex[i]) {
		int y = ver[i];
		if (y != ms[x] && y != fa[x]) dfs2(y, y); 
	}
}

struct Node {
	int l, r; ll val, tag; 
};
Node nd[N << 2]; 

void update(int x) {
	nd[x].val = (nd[x << 1].val + nd[x << 1 | 1].val) % p; 
}

void pushdown(int x) {
	if (nd[x].tag) {
		AddMod(nd[x << 1].tag, nd[x].tag); 
		AddMod(nd[x << 1 | 1].tag, nd[x].tag); 
		AddMod(nd[x << 1].val, nd[x].tag * (nd[x << 1].r - nd[x << 1].l + 1)); 
		AddMod(nd[x << 1 | 1].val, nd[x].tag * (nd[x << 1 | 1].r - nd[x << 1 | 1].l + 1));  
		nd[x].tag = 0;  
	}
}

void Build_tree(int now, int l, int r) {
	nd[now].l = l; nd[now].r = r; 
	if (l == r) { nd[now].val = 1ll * vt[f2[l]]; return; }
	int mid = (l + r) >> 1; 
	Build_tree(now << 1, l, mid); Build_tree(now << 1 | 1, mid + 1, r); 
	update(now);
}

ll Ask(int now, int l, int r) {
	if (nd[now].l == l && nd[now].r == r) return nd[now].val; 
	pushdown(now); 
	int mid = (nd[now].l + nd[now].r) >> 1; 
	if (r <= mid) return Ask(now << 1, l, r); 
	else if (l > mid) return Ask(now << 1 | 1, l, r); 
	else return (Ask(now << 1, l, mid) + Ask(now << 1 | 1, mid + 1, r)) % p; 
}

void Add(int now, int l, int r, ll k) {
	if (nd[now].l == l && nd[now].r == r) { AddMod(nd[now].tag, k); AddMod(nd[now].val, k * (r - l + 1)); return; } 
	pushdown(now); 
	int mid = (nd[now].l + nd[now].r) >> 1; 
	if (r <= mid) Add(now << 1, l, r, k); 
	else if (l > mid) Add(now << 1 | 1, l, r, k); 
	else Add(now << 1, l, mid, k), Add(now << 1 | 1, mid + 1, r, k); 
	update(now); 
}

void Adddist(int x, int y, ll z) {
	while (top[x] != top[y]) {
		if (dep[top[x]] > dep[top[y]]) swap(x, y);
		Add(1, f1[top[y]], f1[y], z); y = fa[top[y]]; 
	}
	if (dep[x] > dep[y]) swap(x, y); 
	Add(1, f1[x], f1[y], z);
}

ll Askdist(int x, int y) {
	ll res = 0; 
	while (top[x] != top[y]) {
		if (dep[top[x]] > dep[top[y]]) swap(x, y);
		AddMod(res, Ask(1, f1[top[y]], f1[y])); y = fa[top[y]];  
	}
	if (dep[x] > dep[y]) swap(x, y); 
	AddMod(res, Ask(1, f1[x], f1[y]));
	return res; 
}

int main() {
	n = read(); m = read(); rt = read(); p = read(); 
	for (int i = 1; i <= n; ++i) vt[i] = read() % p; 
	for (int i = 1; i < n; ++i) { int uu = read(), vv = read(); Addedge(uu, vv); Addedge(vv, uu); }
	dfs1(rt, 0); dfs2(rt, rt); 
	Build_tree(1, 1, n); 
	while (m--) {
		int opt = read(); 
		if (opt == 1) { int x = read(), y = read(); ll z = read(); Adddist(x, y, z); }
		else if (opt == 2) { int x = read(), y = read(); print(Askdist(x, y)); putchar('\n');}
		else if (opt == 3) { int x = read(); ll z = read(); Add(1, f1[x], f1[x] + siz[x] - 1, z); }
		else { int x = read(); print(Ask(1, f1[x], f1[x] + siz[x] - 1)); putchar('\n'); }
	}
	return 0; 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值