树链剖分模板题

Posted 行码棋

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树链剖分模板题相关的知识,希望对你有一定的参考价值。


题目链接:
https://www.luogu.com.cn/problem/P3384

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e5 + 5, M = 2 * N;

int n, m, r, p;

int w[N];

struct Segment

	ll add, sum;
tr[N * 4];

int dep[N], fa[N], son[N], sz[N];
int cnt, nw[N], top[N], id[N];

int e[M], h[N], ne[M], idx;
void add(int a, int b)

	e[idx] = b, ne[idx] = h[a], h[a] = idx++;


void pushup(int u)

	tr[u].sum = (tr[u << 1].sum + tr[u << 1 | 1].sum) % p;

void pushdown(int u, int l, int r)

	int mid = (l + r) >> 1;
	if(tr[u].add)
	
		tr[u << 1].add += tr[u].add;
		tr[u << 1].sum += 1ll * (mid - l + 1) * tr[u].add;
		tr[u << 1].sum %= p;
		tr[u << 1 | 1].add += tr[u].add;
		tr[u << 1 | 1].sum += 1ll * (r - mid) * tr[u].add;
		tr[u << 1 | 1].sum %= p;
		tr[u].add = 0;
	


void build(int u, int l, int r)

	if(l == r)
	
		tr[u].sum = nw[l];
		if(tr[u].sum > p) tr[u].sum %= p;
		return;
	
	int mid = (l + r) >> 1;
	build(u << 1, l, mid);
	build(u << 1 | 1, mid + 1, r);
	pushup(u);


void modify(int u, int l, int r, int x, int y, int d)

	if(l >= x && r <= y)
	
		tr[u].sum += 1ll * (r - l + 1) * d;
		tr[u].sum %= p;
		tr[u].add += d;
		return;
	
	pushdown(u, l, r);
	int mid = (l + r) >> 1;
	if(x <= mid) modify(u << 1, l, mid, x, y, d);
	if(y > mid) modify(u << 1 | 1, mid + 1, r, x, y, d);
	pushup(u);


ll query(int u, int l, int r, int x, int y)

	if(l >= x && r <= y)
		return tr[u].sum % p;
	pushdown(u, l, r);
	int mid = (l + r) >> 1;
	ll ans = 0;
	if(x <= mid) ans = query(u << 1, l, mid, x, y) % p;
	if(y > mid) ans = (ans + query(u << 1 | 1, mid + 1, r, x, y)) % p;
	return ans;


//预处理dep[],fa[],sz[],son[](重儿子节点)
void dfs1(int x, int f, int depth)//x : 当前节点, f:父亲, depth:深度

	dep[x] = depth;
	fa[x] = f;
	sz[x] = 1;
	int mxson = -1;
	for(int i = h[x]; ~i; i = ne[i])
	
		int y = e[i];
		if(y == f) continue;
		dfs1(y, x, depth + 1);
		sz[x] += sz[y];
		if(sz[y] > mxson)// 记录重儿子编号
		
			son[x] = y;
			mxson = sz[y];
		
	


// 标新序号 求id[], nw[], top[] 新id-新节点权重-链顶端
void dfs2(int x, int topf)

	id[x] = ++cnt;
	nw[cnt] = w[x];
	top[x] = topf;
	if(!son[x]) return;//无儿子返回
	dfs2(son[x], topf);
	for(int i = h[x]; ~i; i = ne[i])
	
		int y = e[i];
		if(y == fa[x] || y == son[x])
			continue;
		dfs2(y, y);
	


ll queryRange(int x, int y)

	ll ans = 0;
	while(top[x] != top[y])//不在同一条链上
	
		if(dep[top[x]] < dep[top[y]])
			swap(x, y);
		ans += query(1, 1, n, id[top[x]], id[x]);
		ans %= p;
		x = fa[top[x]];
	
	if(dep[x] > dep[y])
		swap(x, y);
	ans = (ans + query(1, 1, n, id[x], id[y])) % p;
	return ans;


void modifyRange(int x, int y, int d)

	d %= p;
	while(top[x] != top[y])
	
		if(dep[top[x]] < dep[top[y]])
			swap(x, y);
		modify(1, 1, n, id[top[x]], id[x], d);
		x = fa[top[x]];
	
	if(dep[x] > dep[y])
		swap(x, y);
	modify(1, 1, n, id[x], id[y], d);

ll querySon(int x)

	return query(1, 1, n, id[x], id[x] + sz[x] - 1);	

void modifySon(int x, int d)

	modify(1, 1, n, id[x], id[x] + sz[x] - 1, d);


void solve()

	cin >> n >> m >> r >> p;
	for(int i = 1; i <= n; i++)
		cin >> w[i];
	memset(h, -1, sizeof h);
	for(int i = 1; i < n; i++)
	
		int u, v;
		cin >> u >> v;
		add(u, v);
		add(v, u);
	
	dfs1(r, -1, 1);
	dfs2(r, r);
	build(1, 1, n);

	while(m--)
	
		int op, x, y, z;
		cin >> op >> x;
		if(op == 1)
		
			cin >> y >> z;
			modifyRange(x, y, z);
		
		else if(op == 2)
		
			cin >> y;
			cout << queryRange(x, y) << "\\n";
		
		else if(op == 3)
		
			cin >> z;
			modifySon(x, z);
		
		else 
			cout << querySon(x) << "\\n";
	


int main()

	ios::sync_with_stdio(false);
	cin.tie(0);
	int t;
	// cin >> t;
	t = 1;
	while(t--)
		solve();
	return 0;



以上是关于树链剖分模板题的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ 2243 染色 | 树链剖分模板题进阶版

初识树链剖分

树链剖分模板题

树链剖分模板题

树链剖分模板题

BZOJ 1036ZJOI 2008树的统计 树链剖分模板题