树链剖分小结

写在前面:树链剖分是一种高效的数据结构,它的作用是将一棵点权树(一定要是点权树)拆分成链,然后用各种方法来高效解决问题。
树链剖分就是以一种方式遍历树,将原来的树该成一条链,,然后对这条链进行各种高效的操作。

这种方式就是优先遍历最大的子树,我们还是严格的从定义学起。

定义:    

 重儿子:siz[u]为v的子节点中siz值最大的,那么u就是v的重儿子。
    轻儿子:v的其它子节点。
    重边:点v与其重儿子的连边。
    轻边:点v与其轻儿子的连边。
    重链:由重边连成的路径。
    轻链:轻边。

轻重边有几个性质:

性质 1:如果(u, v) 为轻边,则 size(v)  size(u)/ 2 。

证明:设 size(v)  size(u)/ 2 ,则 size(v) 必然比其他儿子的 size 要大,那么(u, v)必然为重边,与(u, v) 为轻边矛盾。

性质 2:从根到某一点 V 的路径上的轻边个数不大于 O(log n) 。

证明:V 为叶子节点时轻边个数最多。

由性质 1 可知,每经过一条轻边,子树的节点个数至少比原来少一半,所以至多经过 O(log n) 条轻边就到达叶子节点了。

性质 3:我们称某条路径为重路径(链),当且仅当它全部由重边组成。那么对于每个点到根的路径上都不超过 O(log n) 条轻边和 O(log n) 条重路径。证明:显然每条重路径的起点和终点都是由轻边构成,而由性质 2 可知,每个点到根节点的轻边个数为 O(log n) ,所以重路径个数也为 O(log n) 。
同时我们也容易发现,一个点在且只在一条重路径上,而每条重路径一定是一条从根结点方向向叶结点方向延伸的深度递增的路径

轻重边剖分的过程可以使用两次 dfs 来实现,有时为了防止递归过深栈溢出,也会使用bfs 的方法来进行剖分。

在这里,当然有很多数组,现在我来分别介绍它们的作用:

top[]数组,用来保存当前节点的所在链的顶端节点

son[]数组,用来保存重儿子

dep[]数组,用来保存当前节点的深度

fa[]数组,用来保存当前节点的父亲

tid[]数组,用来保存树中每个节点剖分后的新编号

siz[]数组,用来保存以x为根的子树节点个数

rank[]数组,用来保存当前节点在线段树中的位置


考虑如何将一条路径(u, v) 拆分为若干条重路径:实际上这个过程就是一个寻找最近公共祖先(LCA)的过程。
考虑暴力的做法,我们会选择u, v 中深度较大的点向上走一步,直到u - v 。现在有了重路径,由于我们记录了重路径的顶部结点top[x],还记录了每个点在序列中的位置,因此我们不需要一步步走。首先我们找出u, v 中top 深度较大的点,假设是u ,则我们可以直接跳到 fa[top[u]]处,且跳过的这一段,在序列中是一段区间,若我们按照深度从小到大来存储点,则这段区间为:pos[top[x]], rank[x]。当u, v 的top 相同时,说明它们走到了同一条重路径上了,这时它们之间的路径也是序列上的一段区间,且u, v 中深度较小的那点是原路径的 LCA。这样我们就可以将给出的任意路径拆分成若干条重路径,也就是若干个区间,并用线段树等数据结构处理操作。
实现:
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=300000;
struct tree{int l,r,v,lazy;}node[2*maxn];
int first[maxn],last[maxn],nxt[maxn],a[maxn],w[maxn],tid[maxn],e[maxn],top[maxn],rank[maxn],size[maxn],r[maxn],tt[maxn],father[maxn],n,m,k,frog,x,y,g,i,s,tot,z;
void add(int x,int y){a[++k]=y;tt[x]++;if(first[x]==0)first[x]=k;else nxt[last[x]]=k;last[x]=k;}

void dfs(int u,int fa)
{
    int i=0;size[u]=1;r[u]=r[fa]+1;
    father[u]=fa;
    for(i=first[u];i;i=nxt[i])
    if(a[i]!=fa)
    {
        dfs(a[i],u);
        size[u]+=size[a[i]];
    }
}
void dfs2(int u,int head)
{
    int i,mson=0;
    top[u]=head;tid[u]=++tot;rank[tid[u]]=tot;change(1,tot,tot,w[u]);
    if(tt[u]==1&&u!=s)
    {
        e[u]=u;
        return;
    }
    for(i=first[u];i>0;i=nxt[i])
    if(size[a[i]]<size[u]&&size[a[i]]>size[mson])mson=a[i];
    dfs2(mson,head);
    e[u]=e[mson];
    for(i=first[u];i>0;i=nxt[i])
    if(size[a[i]]<size[u]&&a[i]!=mson){dfs2(a[i],a[i]);e[u]=e[a[i]];}
}


int main()
{

    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值