长链剖分学习笔记【树链剖分之长链剖分】

  很早的时候,就有了对轻重链剖分的概念,也略微知道一些长链剖分的知识,但一直没有机会用上,所以也不算真正学习了,这次在一场比赛之中遇到了类似的题,虽然用dsu on tree的方法O(N log(N))的解决了,但是想到如果N够大的时候,卡了这个log,那么我们就需要线性O(N)的时间来解决这个问题了。

  类比轻重链剖分,长链剖分的精髓就在于将原来以子树size作为评定轻重的标准,变成了子结点的最远长度,作为划分轻重链的标准,这在一些方面上降低了求解一些问题的复杂度,但是术业有专攻,它却加速了求解有关“深度问题”的时间,做到了线性。

将维护子树中只与深度有关的信息做到线性时间复杂度。——这是长链剖分的一个主要用途

  于是,它就有了一些属于它的性质:

性质

  1. 所有链长度之和是O(N)级别的
  2. 任意一个点的K次祖先y所在的长(重)链的长度一定是大于等于K的
  3. 任意一个点向上跳跃长(重)链的次数不会超过O(sqrt(N))次

由于性质3,所以用长链剖分求LCA的复杂度就是O(\sqrt N )级别的,确实不优于重链剖分。

1、2两个性质比较的好理解,性质3我们需要证明一下,

  :如果一个点从它所在的重链跳到其上的重链,那么新跳到的重链的长度一定会比它原来所在的重链的长度要长,不然为什么之前那个重链不是新重链呢?所以,最坏的重链跳法就是1,2,3,……,\sqrt N,为何?

\frac{(N + 1) * N}{2}N^2级别的,所以我们用\sqrt N来代替原来的N,于是就是N级别的了,最多N个点,所以最多就是跳\sqrt N次。

  所有性质中,最重要的(或许在解决下面的问题的时候)就是性质1,这就使得我们在求解只与深度有关的信息的时候保证了复杂度。

通过举实际的例子,我们来解释这个“主要用途”

题目链接

给你一棵树,定义dx,i表示x子树内和x距离为i的节点数,对每个x求使dx,i最大的i,如有多个输出最小的。

首先,第一步要做的,是预处理的工作,得到“重儿子”。这个操作就比较的简单了,衍生长度最长的就是重儿子。

int len[maxN], Wson[maxN] = {0};
void pre_dfs(int u, int fa)
{
    for(int i=head[u], v; ~i; i=edge[i].nex)
    {
        v = edge[i].to;
        if(v == fa) continue;
        pre_dfs(v, u);
        if(len[v] > len[Wson[u]]) Wson[u] = v;
    }
    len[u] = len[Wson[u]] + 1;
}

那么,接下去,我们怎么处理这个所谓的距离某个点深度为i的点的个数,以及知道这个dx,i呢?

这里,性质一被用上了。

\sum Wlen == N所有重链的长度之和是O(N)级别的。

所以,我们只需要开一条长度为N的数组就可以处理这些信息了,然后给每个长链分配合理的数组上的空间就可以了。

所以,我开了长度为N的tmp数组,作为总的空间,然后呢,我们用指针来解决分配空间的问题。*f[maxN]指向每个点的空间的首地址,譬如说f[u],就是u点对应的首地址,它的分配长度应该是len[u],同时u必须是长链的链头。

然后具体的分配方式,以及处理方式,我们可以看一下代码:

int ans[maxN], tmp[maxN], *f[maxN], *id = tmp;
void dfs(int u, int fa)
{
    f[u][0] = 1; ans[u] = 0;
    if(Wson[u])
    {
        f[Wson[u]] = f[u] + 1;
        dfs(Wson[u], u);
        ans[u] = ans[Wson[u]] + 1;
    }
    for(int i=head[u], v; ~i; i=edge[i].nex)
    {
        v = edge[i].to;
        if(v == fa || v == Wson[u]) continue;
        f[v] = id;
        id += len[v];
        dfs(v, u);
        for(int j=1; j<=len[v]; j++)
        {
            f[u][j] += f[v][j - 1];
            if((j < ans[u] && f[u][j] >= f[u][ans[u]]) || (j > ans[u] && f[u][j] > f[u][ans[u]])) ans[u] = j;
        }
    }
    if(f[u][ans[u]] == 1) ans[u] = 0;
}

完整代码

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <bitset>
//#include <unordered_map>
//#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f3f3f3f3f
#define eps 1e-8
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
#define MP(a, b) make_pair(a, b)
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 1e6 + 7;
int N, head[maxN], cnt;
struct Eddge
{
    int nex, to;
    Eddge(int a=-1, int b=0):nex(a), to(b) {}
}edge[maxN << 1];
inline void addEddge(int u, int v)
{
    edge[cnt] = Eddge(head[u], v);
    head[u] = cnt++;
}
inline void _add(int u, int v) { addEddge(u, v); addEddge(v, u); }
int len[maxN], Wson[maxN] = {0};
void pre_dfs(int u, int fa)
{
    for(int i=head[u], v; ~i; i=edge[i].nex)
    {
        v = edge[i].to;
        if(v == fa) continue;
        pre_dfs(v, u);
        if(len[v] > len[Wson[u]]) Wson[u] = v;
    }
    len[u] = len[Wson[u]] + 1;
}
int ans[maxN], tmp[maxN], *f[maxN], *id = tmp;
void dfs(int u, int fa)
{
    f[u][0] = 1; ans[u] = 0;
    if(Wson[u])
    {
        f[Wson[u]] = f[u] + 1;
        dfs(Wson[u], u);
        ans[u] = ans[Wson[u]] + 1;
    }
    for(int i=head[u], v; ~i; i=edge[i].nex)
    {
        v = edge[i].to;
        if(v == fa || v == Wson[u]) continue;
        f[v] = id;
        id += len[v];
        dfs(v, u);
        for(int j=1; j<=len[v]; j++)
        {
            f[u][j] += f[v][j - 1];
            if((j < ans[u] && f[u][j] >= f[u][ans[u]]) || (j > ans[u] && f[u][j] > f[u][ans[u]])) ans[u] = j;
        }
    }
    if(f[u][ans[u]] == 1) ans[u] = 0;
}
inline void init()
{
    cnt = 0;
    for(int i=1; i<=N; i++) head[i] = -1;
}
int main()
{
    scanf("%d", &N);
    init();
    for(int i=1, u, v; i<N; i++)
    {
        scanf("%d%d", &u, &v);
        _add(u, v);
    }
    pre_dfs(1, 0);
    f[1] = id; id += len[1];
    dfs(1, 0);
    for(int i=1; i<=N; i++) printf("%d\n", ans[i]);
    return 0;
}
发布了884 篇原创文章 · 获赞 1057 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/qq_41730082/article/details/105201787