首页 > 其他分享 >2568. 树链剖分

2568. 树链剖分

时间:2023-01-20 13:11:34浏览次数:60  
标签:2568 剖分 int update mid 树链 重链

2568. 树链剖分

原题链接

思路:

  1. 先将一颗树进行树链剖分,再将它的dfs序求出来
  2. 利用dfs序将线段树模拟出来(build出来)
  3. 再将输入的路径进行切割导入线段树处理,直到两个元素进入同一条重链

注意点

1. top数组的元素记录的是自己所在的重链的顶点,但是如果一个元素是轻儿子,那么就自己做自己的顶点!!一定要记录自己为顶点!!
2. dfs序应当从它的重链开始编号,重链中包含的元素最多使得此条路径在转换为线段树后依旧得到连续的元素最多,使得update_path与query_path中的while循环次数减少,一定程度上降低了程序的时间复杂度。

代码:

#include<bits/stdc++.h>
using namespace std;

#define lc(i) i << 1
#define rc(i) i << 1 | 1

const int N = 1e5 + 7;
typedef long long ll;

int son[N], sz[N], dep[N], f[N], top[N];
int idx[N], nw[N], cnt;

struct tree{
	int l, r;
	ll add, sum;
}tr[N * 4];

int tot, w[N], h[N];
struct edge{
	int ne, to;
}e[N * 2];

void add(int u, int v){
	e[++ tot].to = v;
	e[tot].ne = h[u];
	h[u] = tot; 
}

void dfs1(int now, int fa){
	dep[now] = dep[fa] + 1;
	sz[now] = 1, f[now] = fa;
	for(int i = h[now]; i ; i = e[i].ne){
		int to = e[i].to;
		if(to == fa) continue;
		dfs1(to, now); 
		sz[now] += sz[to];
		if(sz[son[now]] < sz[to])  son[now] = to;
	}
}

void dfs2(int u, int t){
	idx[u] = ++ cnt, nw[cnt] = w[u], top[u] = t;
	if(!son[u]) return;
	dfs2(son[u], t);
	for(int i = h[u]; i ; i = e[i].ne){
		int to = e[i].to;
		if(to == f[u] || to == son[u]) continue;
		dfs2(to, to);
	}
}

void pushup(int i){
	tr[i].sum = tr[lc(i)].sum + tr[rc(i)].sum;
}

void pushdown(int i){
	int k = tr[i].add;
	if(k == 0) return;
	tr[lc(i)].add += k, tr[rc(i)].add += k;
	tr[lc(i)].sum += (tr[lc(i)].r - tr[lc(i)].l + 1) * k;
	tr[rc(i)].sum += (tr[rc(i)].r - tr[rc(i)].l + 1) * k;
	tr[i].add = 0;
}

void build(int l, int r, int i){
	tr[i] = {l, r, 0, 0};
	if(l == r){
		tr[i].sum = nw[l];
		return;
	}
	int mid = (l + r) >> 1;
	build(l, mid, lc(i));
	build(mid + 1, r, rc(i));
	pushup(i);
}

void update(int x, int y, int i, int k){
	if(x <= tr[i].l && tr[i].r <= y){
		tr[i].sum += (tr[i].r - tr[i].l + 1) * k;
		tr[i].add += k;
		return;
	}
	pushdown(i);
	int mid = (tr[i].l + tr[i].r) >> 1;
	if(mid >= x) update(x, y, lc(i), k);
	if(mid < y) update(x, y, rc(i), k);
	pushup(i);
}

ll query(int x, int y, int i){
	if(x <= tr[i].l && tr[i].r <= y) return tr[i].sum;
	pushdown(i);
	ll res = 0;
	int mid = (tr[i].r + tr[i].l) >> 1;
	if(mid >= x) res += query(x, y, i << 1);
	if(mid < y) res += query(x, y, i << 1 | 1);
	return res;
}

void update_path(int x, int y, int k){
	while(top[x] != top[y]){
		if(dep[top[x]] < dep[top[y]]) swap(x, y);
		update(idx[top[x]], idx[x], 1, k);
		x = f[top[x]];
	}
	if(dep[x] < dep[y]) swap(x, y);
	update(idx[y], idx[x], 1, k);
}

ll query_path(int x, int y){
	ll res = 0;
	while(top[x] != top[y]){
		if(dep[top[x]] < dep[top[y]]) swap(x, y);
		res += query(idx[top[x]], idx[x], 1);
		x = f[top[x]];
	}
	if(dep[x] < dep[y]) swap(x, y);
	return res + query(idx[y], idx[x], 1);
}


int main(){
	int n, m;
	scanf("%d", &n);
	for(int i = 1;i <= n;i ++) scanf("%d", &w[i]);
	for(int i = 1;i < n;i ++){
		int x, y;
		scanf("%d%d", &x, &y);
		add(x, y), add(y, x);
	}
	dfs1(1, 0);
	dfs2(1, 1);
	scanf("%d", &m);
	build(1, n, 1);   
	int op, u;
	while(m --){
		scanf("%d%d", &op, &u);
		if(op == 1){
			int v, k;
			scanf("%d%d", &v, &k);
			update_path(u, v, k);
		}
		else if(op == 2){
			int k;
			scanf("%d", &k);
			update(idx[u], idx[u] + sz[u] - 1, 1, k);
		}
		else if(op == 3){
			int v;
			scanf("%d", &v);
			printf("%lld\n", query_path(u, v));
		}
		else printf("%lld\n", query(idx[u], idx[u] + sz[u] - 1, 1));
	}
	return 0;
}

标签:2568,剖分,int,update,mid,树链,重链
From: https://www.cnblogs.com/loser--QAQ/p/17062690.html

相关文章

  • 算法学习笔记(6): 树链剖分
    树链剖分树链剖分是一个很神奇,但是在树上可以完成一些区间操作问题简单来说,就是把一棵树分成一条条的链,通过维护链上的信息来维护整棵树的信息基础知识可以参考我的另外......
  • 树链剖分 学习笔记
    树链剖分是一种思想,可以用来通过给树中每个点重新编号,从而让树中任意一条路径转化成\(O(logn)\)段连续区间(链)。树中任意一条路径均可以变成不超过\(logn\)段连续区间......
  • P2146 [NOI2015] 软件包管理器 树链剖分
    //题目大意:给定一棵树,树上的每个节点是一个软件,现在给出如下两个指令,install与uninstall,//如果需要installx号软件,那么我需要安装他到根节点之间的所有软件;如......
  • ZJOI2008, 树的统计 树链剖分模板
    //题意:给定一棵树,现在我需要询问以下操作//1.q,u之间的最小值//2.q,u之间的简单路径的权值和//3.修改树上q点的权值//思路:如果是在一段序列上的问......
  • 处理手法学习(树链剖分)
    今天vp了两场cf,感觉对开眼界还是很有用的,手感也回来了点首先给出一些点,如何找出是否属于同一条链首先暴力方法就是每次dfs,在分叉大于2的地方看看是否包含所有的点这是个......
  • BZOJ 3252 攻略(长链剖分模板 链的常用性质 贪心)
    BZOJ3252攻略​ 给定一棵带边权的树,选择k个叶子结点,使这些叶子结点与根节点相连,形成k条路径。输出被路径覆盖的所有边的边权和的最大值(同一条边若被重复覆盖只算一......
  • 最大面积最小的三角剖分 Minimax Triangulation
    #include<iostream>#include<algorithm>#include<cmath>#include<vector>usingnamespacestd;#definemistake(P)(fabs(P)<eps?0:(P<0?-1:1))......
  • 边权树链剖分
    边权树链剖分一般的树链剖分都是维护的点权,而如果要维护边权怎么办呢?思路我们可以把边权转换为点权。一个点有很多个儿子,但只有一个父亲。如果一个节点的点权记录到其儿......
  • 重链剖分学习笔记
    最近在机房自习复习了一下这些东西,主要给自己看的。参考文献:OIWiki《算法竞赛》(罗勇军,郭卫斌著)1重链剖分简介1.1重链剖分基本性质树链剖分,就是把树分成若......
  • 重链剖分学习笔记
    最近在机房自习复习了一下这些东西,主要给自己看的。参考文献:OIWiki《算法竞赛》(罗勇军,郭卫斌著)1重链剖分简介1.1重链剖分基本性质树链剖分,就是把树分成若......