Lca树链剖分法

树链剖分各数组的作用:
son[]:最重的儿子节点,即节点最多的那个
bulk[]:所有儿子节点的总和(包括儿子的儿子)
dep[]:该节点的深度
ft[]:该节点的父亲
top[]:链的最上方的节点

首先Dfs预处理出前面的四个数组。
然后第二个Dfs进行剖分,预处理出最后一个数组。

例子:
这里写图片描述
此时son[1]=3(因为bulk[3]=6>bulk[2]=5),son[3]=10,son[4]=5,以此类推。

然后剖分之后相当于变成了
这里写图片描述
此时top[10]=1,top[7]=7,top[8]=9,以此类推

接下来就是进行Lca,假设c=lca(a,b),首先,当a,b在同一条重链中时,c=min(dep[a],dep[b]),当a,b不在同一条重链中时,画图易知c一定是划分轻重链的的那个分支点,比如图中的1,3,4。假设我们现在要求lca(7,8),那么lca(7,8)一定是某条直到7,8在同一条重链时的分支点,所以不断地把7,8向上合并到重链中,直到他们在同一条重链中。

现在进行模拟,先看看代码中的Lca,方便理解。令x=7,y=8,因为dep[top[x]]>dep[top[y]](top[x]=7,top[y]=9),所以更新x=4,发现top[x](top[x]=2),top[y](top[y]=9)不在一条重链中,继续更新,此时dep[top[y]]大,更新y=3,因为dep[top[x]]大,更新x=1,发现他们已经在同一条重链中,那么深度小的即为lca(7,8)

附模板:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int MAXN = 1000;

struct Edge
{
    int v, next;
    Edge(int v=0,int next=0):v(v),next(next){}
}edge[MAXN];

int head[MAXN], edgenum;//邻接表
int son[MAXN], bulk[MAXN], dep[MAXN], ft[MAXN], top[MAXN];

void toInit()
{
    memset(head, -1, sizeof(head));
    edgenum = 0;
}

void toAdd(int u,int v)
{
    edge[edgenum] = Edge(v, head[u]);
    head[u] = edgenum++;
}

void toDfs1(int u,int f,int tier)
{
    son[u] = 0;dep[u] = tier;ft[u] = f;bulk[u] = 1;
    for (int i = head[u];i != -1;i = edge[i].next)
    {
        int v = edge[i].v;
        if (v == f) continue;
        toDfs1(v, u, tier + 1);
        bulk[u] += bulk[v];
        if (bulk[v] > bulk[son[u]])//son[]更新为重儿子
            son[u] = v;
    }
}

void toDfs2(int u,int f,int rt)
{
    top[u] = rt;
    for (int i = head[u];i != -1;i = edge[i].next)
    {
        int v = edge[i].v;
        if (v == f) continue;
        if (v == son[u])//链剖分
            toDfs2(v, u, rt);//重链top继承父亲的top
        else
            toDfs2(v, u, v);//轻链top为自身
    }
}

int toLca(int x,int y)
{
    while (top[x] != top[y])
    {
        if (dep[top[x]] < dep[top[y]])
            swap(x, y);//使深度大的始终为x
        x = ft[top[x]];
    }
    return dep[x] < dep[y] ? x : y;
}

int main()
{
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值