树链剖分自整理

参考博客:https://blog.csdn.net/qq_18661257/article/details/52766738

树链剖分主要针对路径进行处理,我们平时处理一颗树的路径的方案一般就是求解LCA的各类算法,在线ST,离线tarjan,以及LCA在线倍增法,其实LCA在线倍增法在求解路径方面已经颇为让人满意了,O(nlogn)O(nlogn)构建,O(logn)O(logn)进行查询,比如一般的求解路径上的最大边权,点权之类的一般LCA倍增法都可以解决,如果再离线一下,或许复杂度更低。

但是树链剖分的独特之处就是可以对路径进行修改,一般结合线段树进行处理,查询更新的复杂度就是O(logn),这是LCA在线倍增所不能做到的。

树链剖分基本性质

  1. 如果(v,u)(v,u)为轻边,则ve[u]×2<ve[v]ve[u]×2<ve[v];
  2. 从根到某一点的路径上轻链、重链的个数都不大于lognlogn。

相关数组定义

deep[u]:来保存当前节点uu的深度

fa[u]: 用来保存当前节点uu的父亲

ve[u]: 用来保存以uu为根的子树节点个数

son[u]: 用来保存当前节点uu的重儿子

top[u]:用来保存当前节点uu的所在链的顶端节点

p[u]: 用来保存当前节点uu在线段树中的位置

fp[]:用来保存线段树相应位置保存的是当前哪个节点

 

dfs1是为了找重边,记录下所有的重边以及相关关系

void dfs1(int u,int p,int d)
{
	deep[u] = d;
	fa[u] = p;
	ve[u] = 1;
	son[u] = -1;
	for(int i = head[u];i!= -1;i = s[i].ne)
	{
		if(s[i].v == p) continue;
		dfs1(s[i].v,u,d+1);
		ve[u]+= ve[s[i].v];
		if(son[u] == -1||ve[s[i].v]> ve[son[u]])
			son[u] = s[i].v;
	}
	return ;
}

dfs2就是为了连接重边形成重链,即将这条长路径给连接起来,具体步骤:以根节点为起点,沿着重边向下拓展,拉成重链,不在当前重链上的节点,都以该节点为起点向下重新拉一条重链。

void dfs2(int u,int sp)
{
	top[u] = sp;
	p[u] = ++sz;
	fp[p[u]] = u;
	if(son[u] == -1) return ;
	dfs2(son[u],sp);
	for(int i = head[u];i!= -1;i = s[i].ne)
	{
		if(s[i].v == son[u]||s[i].v == fa[u]) continue;
		dfs2(s[i].v,s[i].v);
	}
	return ;
}

查询两点之间路径权值和为例

ll demand(int x,int y)
{
	int fx = top[x];
	int fy = top[y];
	ll ans = 0;
	while(fx!= fy)
	{
		if(deep[fx]< deep[fy])
		{
			swap(fx,fy);
			swap(x,y);
		}
		ans+= query(1,p[fx],p[x]);
		x = fa[fx];
		fx = top[x];
	}
	if(x == y) return ans;
	if(deep[x]> deep[y]) swap(x,y);
	ans+= query(1,p[son[x]],p[y]);
	return ans;
}

分析:

  1. 如果u与v在同一条重链上,那么就直接修改了,因为他们是连续的
  2. 如果不在一条重链上,则往一条重链上靠拢,然后就会编程一条重链

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值