首页 > 其他分享 >树链剖分学习笔记

树链剖分学习笔记

时间:2023-05-04 18:36:07浏览次数:36  
标签:hson 剖分 int siz 笔记 树链 100001 重链

一棵树,支持:

  1. 路径加
  2. 单点查询

一般树上链的问题使用树链剖分解决。

重链剖分

前置知识

LCA,线段树

定义

重儿子:所有儿子中子树最大的儿子为重儿子

重边:重儿子之间的连边

重链:若干重儿子连成的链

性质

一棵树可以被剖成若干重链。

优先遍历重儿子,所有重链的dfs序连续。

重链数量不多于 \(\log_2\) 个。

一条链上经过的重链个数不多于 \(\log_2\) 个。

我们可以通过线段树维护一段重链的信息。

枚举链上的重链,查询可以做到 \(\log^2\)。

实现

预处理

int fat[100001],siz[100001],dep[100001],hson[100001],top[100001],cnt,dfn[100001],dis[100001];
void getdfsh(int u,int fa) //获取:父节点,深度,子树大小,重儿子 
{
    fat[u]=fa;
    dep[u]=dep[fa]+1;
    int lll=0;
    for(int i=0;i<g[u].size();i++)
    {
        int v=g[u][i];
        if(v==fa) continue;
        getdfsh(v,u);
        if(siz[v]>lll)
        {
            hson[u]=v;
            lll=siz[v];
        }
        siz[u]+=siz[v];
    }
    siz[u]++;
}
void gettd(int u,int fa) //获取:链顶,dfs序 
{
    dfn[u] = ++cnt;
    dis[u] = cnt;
    if(hson[fat[u]]==u)
    {
        top[u]=top[fa];
    }
    else
    {
        top[u]=u;
    }
    if(hson[u]!=0) gettd(hson[u],u); //优先重子 
    for(int i=0;i<g[u].size();i++)
    {
        int v=g[u][i];
        if(v==fa||v==hson[u]) continue;
        gettd(v,u);
    }
}

修改与查询

int addlsum(int st,int ed,int v) //修改一条链,满足 lca(st,ed)=ed
{
	if(dfn[st]<dfn[ed]) swap(st,ed); //交换
	int u=st,re=0;
	while(1)
	{
		if(dfn[top[u]]<=dfn[ed]) //当前重链顶超过终点了
		{
			add(1,1,n,dfn[ed],dfn[u],v);
			break; //跳出
		}
		add(1,1,n,dfn[top[u]],dfn[u],v);
		u=fat[top[u]];
	}
	return re;
} 
int getlsum(int st,int ed) //查询一条链的和 同上
{
	if(dfn[st]<dfn[ed]) swap(st,ed);get(1,1,n,dfn[ed],dfn[st]);
	int u=st,re=0;
	while(1)
	{
		if(dfn[top[u]]<=dfn[ed])
		{
			re+=get(1,1,n,dfn[ed],dfn[u]);
			break;
		}
		re+=get(1,1,n,dfn[top[u]],dfn[u]);
		u=fat[top[u]];
	}
	return re;
}

长链剖分

长链即按深度剖分,主要解决深度问题,如 \(k\) 级祖先。

标签:hson,剖分,int,siz,笔记,树链,100001,重链
From: https://www.cnblogs.com/lizhous/p/17372165.html

相关文章

  • 生成函数学习笔记
    概念序列的母函数(生成函数)是一种形式幂级数。其每一项的系数可以提供关于这个序列的信息,使用母函数解决问题。如:序列\(a\)的生成函数为\(G(x)=\sum\limits_{i=1}^{n}a_if_i(x)\)。其中\(f_i(x)\)是无实际意义的,具体取值看题目要求。但有一些一般取值。一般生成函数令\(f......
  • 拉格朗日插值学习笔记
    拉格朗日插值学习笔记概念拉格朗日插值用于拟合一个函数。可以通过已知函数中的点拟合出函数。若为\(n\)次函数,则需要多于\(n+1\)个点。做法考虑构造\(n+1\)个函数,第\(i\)个函数\(f_i\)对应点\(i\)满足\(f_i(X_i)=Y_i\)且对于其他的点\(j(i\neqj)\)满足\(f_......
  • 学习笔记:数位dp
    1.基本模型数位dp,即以数的每一位作为状态进行dp的算法。通常状态为\(f_{i,0-9}\)表示第\(i\)为取\(0-9\)时的dp值。通常时间复杂度为\(log_{10}n\),十分优秀。2.套路求区间合法类的题,使用容斥思想思想求解,即\([1,r]-[1,l-1]\)dp式子一般很简单,可以使用矩阵快速幂优化......
  • 线性基学习笔记
    概念线性基是一个集合。从原集合中选取任意数都能通过线性基中的数异或得到。本质上是对集合的压缩性质所有数字没有最高位相同的集合大小为\(\log_2\)级别。操作排查:若线性基内有最高位相等的,让其相异或,并继续排查直到没有可操作的数。若原集合内有\(0\)线......
  • 莫队学习笔记
    概念莫队是一种幽雅的暴力。用于处理区间问题。核心思想就是把询问离线下来,然后维护双指针按一定顺序处理每个询问。精髓就在于一定顺序。首先确定一个块长,然后将左端点的位置除以块长,把询问分成若干块。在每个块里按右端点排序。发现当块长为\(\sqrtn\)时两个指针各移动\(......
  • 后缀数组学习笔记
    概念后缀数组,即对于一个串,它的每个后缀按字典序排序后得到的数组。有两个数组要求:\(SA_i\):排名为\(i\)的后缀的开头位置\(RK_i\):以\(i\)为开头的后缀的排名朴素sort排序一下优化倍增优化:我们进行\(\logn\)次排序,第\(k\)次取所有后缀的前\(2^k\)个字符进行......
  • 点分治学习笔记
    概念点分治用于解决有一定要求的链的计数。对于点\(u\)的子树的问题,可以将答案分为:经过点\(u\)不经过点\(u\)第一种可以用桶加暴力。枚举一端的长度,用桶计算另一端长度;第二种分到子树中解决即可。注意到,在随机选根的时候该算法表现不优秀,但若根为重心,因为每次子树......
  • 网络流学习笔记
    概念最大流:在一个网络图上,每个边有流量限制,假如起始点有无线流量,求最多能有多少流量流到终点。增广路:一条从起始点到终点了路径,可以流流量。算法Ford-Fulkerson算法解决这个问题,可以用Ford-Fulkerson算法。该算法的核心就是寻找增广路。每找到一条增广路,就给它它能承受......
  • TypeScript 学习笔记 — 模板字符串和类型体操(十五)
    目录基本介绍字符串类型体操实操环节1.字符串首字母大写CapitalizeString2.获取字符串第一个字符FirstChar3.获取字符串最后一个字符LastChar4.字符串转元组StringToTuple5.元组转字符串TupleToString6.重复字符串RepeatString7.字符串分割SplitString8.获取字符串......
  • react.js学习笔记
    (1)      参考文献1.前端技术手册2.在线编码......