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

树链剖分

时间:2024-05-21 12:18:14浏览次数:9  
标签:sz 剖分 int top 树链 dep dfn 节点

树链剖分

把一棵树剖分成若干条链,然后对链进行操作,维护路径上的信息。

有点像分块,也是暴力做法,单个操作太慢就整个操作。

我们一般用 重链剖分 ,也就是根据子树大小把边分成轻重边。

重链剖分

定义

重儿子:所有子节点中子树最大的节点,多个一样的取任意。

轻儿子:不是重儿子的就是轻儿子。

重边:从该节点到重儿子的边。

轻边:从该节点到轻儿子的边。

重链:重边首尾相连组成的链。

性质

  1. 剖分时我们先遍历重儿子,最终 \(dfs\) 序压树,出来的区间重链的区间是连续的。

  2. 任意一个节点到根节点最多走 \(O(logN)\) 条重链,
    所以任意两个节点之间的路径最多包含 \(O(2log(N))\) 条重链
    (感性证明:每次分轻重链都会把子树分成两半,最多 \(log_2N\) 条,两个节点之间的路可以看成两条到根的路)


(很清楚的一张图)

实现

维护的值(一下变量名为个人习惯命名)

1 \(sz\) 子树大小
2 \(dep\) 节点深度
3 \(fa\) 父节点
4 \(son\) 重儿子
5 \(top\) 所在重链的端点(深度最小)
6 \(dfn\) \(dfs\)序
7 \(rk\) \(dfs\)序对应的节点标号

\(dfs\) 两次,第一次找 \(son\),维护\(sz,dep,fa\),找出重儿子。

第二次更新 \(top,dfn,rk\),记录 \(dfs\)序。

int son[N],fa[N],sz[N],dep[N],rk[N],dfn[N],cnt,top[N];
void dfs1(int u,int f)
{
	son[u]=-1; sz[u]=1; dep[u]=dep[f]+1; fa[u]=f;
	for(int i=head[u];i;i=e[i].u)
	{
		int v=e[i].v;
		if(v==f) continue; 
		dfs1(v,u); sz[u]+=sz[v];
    	//a[v]=e[i].w; 边权改点权
		if(son[u]==-1||sz[son[u]]<sz[v]) son[u]=v;
	}
}
void dfs2(int u,int t)
{
	top[u]=t; dfn[u]=++cnt; rk[cnt]=u;
	if(son[u]==-1) return; dfs2(son[u],t);
	for(int i=head[u];i;i=e[i].u)
	{
		int v=e[i].v;
		if(v!=son[u]&&v!=fa[u]) dfs2(v,v);
	}
}//dfs

查询及修改

有点像 lca ,凭借重链往上跳,同时进行区间操作。

void mdfpath(int x,int y,int v)
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		mdf(1,dfn[top[x]],dfn[x],v);
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	mdf(1,dfn[x],dfn[y],v);//边权这里dfn[x]+1
}

int quepath(int x,int y)
{
	int res=0;
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		res+=quesum(1,dfn[top[x]],dfn[x]);
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	res+=quesum(1,dfn[x],dfn[y]);//边权这里dfn[x]+1
	return res;
}

(码量主要在线段树,也可以用树状数组)

应用

树上路径问题转化为区间问题。

标签:sz,剖分,int,top,树链,dep,dfn,节点
From: https://www.cnblogs.com/ppllxx-9G/p/18203679

相关文章

  • 树链剖分
    树链剖分额一个怎么说感觉很鸡肋的东西,它似乎就是普通线段树加上了个路径上的修改和查询(暂时所学),但是肯定比线段树灵活。不多说,先看成果:::如何处理树上一个点到另一个点链上的操作?如果考虑暴力的话,肯定是不可行的,因为当这个树变成类似一条链的时候,我们的复杂度就可能被卡到惊人......
  • 树链剖分代码细解
    总结回顾类文章,酌情观看。ShapeOfYou头图Linux找图太难了呜呜Theclubisn'tthebestplacetofindaloverSothebariswhereIgoMeandmyfriendsatthetabledoingshotsDrinkingfasterandthenwetalkslowYoucomeoverandstartupaconversat......
  • 树链剖分 学习笔记
    裂缝中的阳光-林俊杰头图有多少创伤卡在咽喉有多少眼泪滴湿枕头有多少你觉得不能够被懂的痛只能沉默有多少夜晚没有尽头有多少的寂寞无人诉说有多少次的梦还没做已成空等到黑夜翻面之后会是新的白昼等到海啸退去之后只是潮起潮落别到最后你才发觉心里头的......
  • 树链剖分 学习笔记
    树链剖分学习笔记时更。还没开始学,放个板子先。板子#include<bits/stdc++.h>#definefo(x,y,z)for(int(x)=(y);(x)<=(z);(x)++)#definefu(x,y,z)for(int(x)=(y);(x)>=(z);(x)--)typedeflonglongll;inlineintqr(){ charch=getchar();intx=0,f=1; for(;ch......
  • 长链剖分
    我们现在有一棵有根树(节点的深度定义为根到它的距离)。设节点\(u\)的所有儿子中,深度最大的点为它的长儿子,记作\(son_u\)。(存在多个则任取一个,没有儿子则为空)记每个节点到它的长儿子的变为长边,其余边为短边。一段极长的全部由长边组成的链称为长链。特别的,按此过程划分后,不在......
  • 树链剖分
    树链剖分在DFS树上把连续的一段有祖先关系的单独开一个序列存储。查询每一个位置,不断地往链头条,然后跳到链头的父亲的链上\(\dots\)如果按DFS徐直接搞,会被以下数据hack可行的序列有\(:[110],[2,10],[3,12],[4,13],[5,14],[6,15],[7,16],[8,17],......
  • 树链剖分
    树链剖分我们一般使用重链剖分,就是子树大的先剖分,应为这样可以保证时间在\(log_n\)如图,先按照\(dfs\)序遍历出有一棵树,那么\(dfs\)序就是\([1,2,3,4,5,6,7,8,9]\),如果碰到一条边上\(dfn[f]-dfn[u]!=1\)则断一次,就可以剖出链了,如图,链为\([1,2,3][4,5]......
  • 树链剖分
    树链剖分,简称树剖,就是把一颗又大又高的树拆成一些链,方便使用某些数据结构。一般树剖我们随便DFS一下,将整棵树分成一些链,其中里面的DFS序连续。链的数量不管怎样是固定的\(O(N)\)。hack:某种DFS序是\((1,3,2,5,4,7,6,9,8,11,10)\),只要你不走运刚好,就仍然可以把单次询......
  • 重链剖分题目选讲
    染色给定一棵\(n\)个节点的无根树,共有\(m\)个操作,操作分为两种:将节点\(a\)到节点\(b\)的路径上的所有点(包括\(a\)和\(b\))都染成颜色\(c\)。询问节点\(a\)到节点\(b\)的路径上的颜色段数量。颜色段的定义是极长的连续相同颜色被认为是一段。例如112221由......
  • 树链剖分
    link1link2前言:树链剖分实际上就是一种将树形结构剖分成一条条链状结构,并用线性数据结构来快速维护信息。重链剖分:一些定义:重儿子:一个节点的重儿子定义为它的子节点中子树节点最大的节点。轻儿子:一个节点除重儿子外的所有儿子重边:一个节点到它的重儿子的边即为重边......