ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

树链剖分学习笔记(二)

2021-11-18 01:04:16  阅读:169  来源: 互联网

标签:长链 剖分 int top 笔记 树链 祖先 节点


上一篇:树链剖分学习笔记(一)

这篇是长链剖分
并没有仔细研究过这方面的内容,所以就随便写点简单的东西了

1. 概念

长链剖分也是一种树链剖分,所以和轻重链剖分很相似
区别是长链剖分选择子树深度最大的儿子作为重儿子,而不是子树大小最大的

它也具有一些性质:

  1. 链长总和是 \(O(n)\) 级别的
    好像是句废话

  2. 任意节点 \(x\) 的 \(k\) 级祖先 \(y\) 所在的长链的长度大于等于 \(k\)
    \(y\) 到 \(x\) 这条链的长度为 \(k\) ,而显然 \(y\) 所在的长链不可能比它短

  3. 任意节点 \(x\) 到根节点的路径最多包含 \(O(\sqrt n)\) 条长链
    从 \(x\) 向上跳,若到达新的长链上,则该链的长度必然大于之前那条链的长度
    所以最坏情况下经过的长链的长度依次为 \(1,2,3,\cdots\)
    链长总和为 \(O(n)\) 级别,故链的条数为 \(O(\sqrt n)\) 级别

2. 应用

这里只讲一道例题 因为只做了一道

[Vijos]lxhgww的奇思妙想

题意:求节点 \(x\) 的 \(k\) 级祖先 \(y\),多组询问,强制在线
算个模板题吧

令 \(h={\rm highbit}(k)\) 表示 \(k\) 的最高二进制位,则 \(h>k/2\)

倍增预处理所有 \(2^a\) 级祖先,则回答询问时我们可以先直接跳到 \(x\) 的 \(h\) 级祖先 \(z\)
然后 \(z\) 所在的长链长度大于等于 \(h\)
那么 \(k\) 级祖先应该就在这条链或者其上端的那条长链上
凭直觉感受的话这东西应该能 \(O(1)\) 求了

设 \(z\) 所在长链长度为 \(len\) ,顶端节点为 \(top\)
从 \(z\) 开始还需向上跳 \(k-h\) 次
故 \(y\) 与 \(top\) 的距离为 \((dep_z-dep_{top})-(k-h)\)
距离为正则 \(y\) 是 \(top\) 的儿子,为负则 \(y\) 是 \(top\) 的祖先

对于每个 \(top\) ,向上预处理前 \(len\) 级祖先,向下处理前 \(len\) 级儿子
于是就实现了 \(O(1)\) 查询
而这一步预处理的时间复杂度为 \(O(\sum len)\) ,也就是 \(O(n)\)

总时间复杂度为 \(O(n\log n+m)\)
可以发现倍增预处理是复杂度瓶颈,故只有像本题这样询问量很大时长链剖分才具有优势

#include<stdio.h>
#include<ctype.h>
#include<vector>
#define gc (l==r&&(r=(l=c)+fread(c,1,1<<21,stdin),l==r)?EOF:*l++)
const int N=300010; int n,m,lgn,cnt,x,y,k,len,t,ans;
int last[N],dep[N],depm[N],top[N],son[N],hb[N];
struct node { int y,pre; }E[N<<1];
std::vector<int>a[N],b[N]; int sa[N],sb[N],f[N][20];
void dfs(int x,int fa) {
    depm[x]=dep[x]=dep[fa]+1,f[x][0]=fa; // depm: x 的子树中节点的最大深度
    for (int i=0; i<lgn; ++i)
        f[x][i+1]=f[f[x][i]][i]; // 倍增预处理
    for (int i=last[x],y; i; i=E[i].pre)
        if ((y=E[i].y)!=fa) {
            dfs(y,x);
            if (depm[y]>depm[son[x]])
                son[x]=y,depm[x]=depm[y];
        }
}
void dfs2(int x,int tx) {
    top[x]=tx;
    if (!son[x]) return ;
    dfs2(son[x],tx);
    for (int i=last[x],y; i; i=E[i].pre)
        if (!top[y=E[i].y]) dfs2(y,y);
}


int p=-1,p2,num[9];
char c[1<<21],*l=c,*r=c,cw[1<<21];
int read() { // fread 读入优化
    int x=0; char ch=gc;
    while (!isdigit(ch)) ch=gc;
    while (isdigit(ch)) x=x*10+(ch^48),ch=gc;
    return x;
}
inline void flush() { fwrite(cw,1,p+1,stdout),p=-1; }
void write(int x) { // fwrite 输出优化
    p>>20&&(flush(),0);
    do num[++p2]=x%10; while (x/=10);
    do cw[++p]=(num[p2]^48); while (--p2);
    cw[++p]='\n';
}
int main() {
    n=read(),lgn=-1;
    for (int i=1; i<=n; i<<=1) ++lgn;
    for (int i=1; i<n; ++i)
        x=read(),y=read(),
        E[++cnt]={y,last[x]},last[x]=cnt,
        E[++cnt]={x,last[y]},last[y]=cnt;
    dfs(1,0),dfs2(1,1);
    for (int i=1,j=0; i<=n; ++i) // 预处理 highbit
        i==(1<<j+1)&&(++j),hb[i]=j;
    for (int i=1; i<=n; ++i) if (top[i]==i) {
        len=depm[i]-dep[i];
        for (int j=0,t=i; j<=len; ++j,t=f[t][0])
            a[i].push_back(t); // 祖先
        for (int j=0,t=i; j<=len; ++j,t=son[t])
            b[i].push_back(t); // 儿子
        sa[i]=a[i].size(),sb[i]=b[i].size();
    }
    m=read();
    while (m--) {
        x=read()^ans,k=read()^ans;
        if (k) {
            x=f[x][hb[k]],y=top[x];
            t=dep[x]-dep[y]-k+(1<<hb[k]);
            if (t<0) ans=(-t>=sa[y]?0:a[y][-t]);
            else ans=(t>=sb[y]?0:b[y][t]);
        }
        else ans=x;
        write(ans);
    }
    return flush(),0;
}

标签:长链,剖分,int,top,笔记,树链,祖先,节点
来源: https://www.cnblogs.com/REKonib/p/15569999.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有