关于树链剖分

树链剖分

树链剖分可以解决树上路径,子树之类的一系列问题。

下面以[LuoguP3384]为例,讲解关于树链剖分的部分操作。

主要思想

树剖通过一种特殊的枚举方法,将树上的路径转化成连续的几段,通过线段树等操作去维护。

预处理

树链剖分的主要通过两个dfs求出以下需求的值

  • depth[x]:x的深度

  • fa[x]:x的父节点

  • sum[x]:包括节点x的子树一共有多少个节点

  • heavy_son[x]:x的重儿子

  • top[x]:x所在重链的首部

  • id[x]:x节点在线段树中对应的下标

下面对于一些量做出解释

重儿子:x的儿子中sum[x]最大的那个节点

重链:在树链剖分中,为了将一棵树剖分成连续的线段,通常采用从根节点开始,先访问重儿子,再访问其它儿子的方式,并将访问的时间顺序依次在线段树中将树上节点对应线段树的点1,2,3...n。

这样操作一条重链上的点所对应的线段树的节点就是连续的(即id[x]是连续的) 重链即表示在这条链上的节点编号是连续的。即top[x]到x的路径的点编号是连续的。(特殊的,1个点也算一条重链)

虽然量比较多,我也每次看到这里就不想学,但不用着急,等介绍了如何求后再去理解就不难了。

如何预处理

第一个dfs,求出depth,fa,sum,heavy_son

这应该是老套路了,简单求解。

void dfs1(int x,int father)
{
	int maxson=0,SON=0;
	depth[x]=depth[father]+1;fa[x]=father;sum[x]=1;
	for(int k=lnk[x];k;k=las[k])
	if(nex[k]!=father)
	{
		dfs1(nex[k],x);
		sum[x]+=sum[nex[k]];
		if(sum[nex[k]]>maxson)maxson=sum[nex[k]],SON=nex[k];
	}
	heavy_son[x]=SON;
}

第二个dfs,求出id,top

id数组按照枚举点的顺序就行了

关于求top,如果当前访问的是x的重儿子,top[x的重儿子]就和top[x]是一样的。(因为按照先重儿子的顺序top[x]到x的重儿子的编号是连续的,如果不是重儿子,那么top[x的重儿子]就是x的重儿子。(新的一段连续编号)

void dfs2(int x,int TOP)
{
	id[x]=++cnt;newt[cnt]=t[x];top[x]=TOP;
	if(heavy_son[x])dfs2(heavy_son[x],TOP);
	for(int k=lnk[x];k;k=las[k])
	if(nex[k]!=fa[x]&&nex[k]!=heavy_son[x])dfs2(nex[k],nex[k]);
}
原文地址:https://www.cnblogs.com/DavidJing/p/10426543.html