首页 > 其他分享 >树链剖分|树上启发式合并

树链剖分|树上启发式合并

时间:2024-10-11 20:48:55浏览次数:6  
标签:fu 剖分 int top 树链 dfn fv 启发式 重链

树链剖分

分为重链剖分和长链剖分以及其他奇怪的剖分。以重剖为主。

重链剖分

将树上问题重链剖分为序列问题(经常是 DFS 序)然后用数据结构(经常是线段树)维护。

剖分部分

定义:

  • 重儿子:对于一个点,其儿子中,子树最大的那个;
  • 重边:父亲到重儿子的连边;
  • 轻儿子:除了重儿子以外的儿子;
  • 轻边:父亲到轻儿子的连边。

证明一个东西:一条链上(不是路径,虽然差不多,路径就是两条链拼起来)至多有 \(\log n\) 条轻边。

很显然,每次若是走轻边,子树大小都至少会减少一半,所以至多走 \(\log n\) 次就到底了。

于是我们即使暴力维护轻边的复杂度都是 \(O(F(n)\log n)\) 的,\(F(n)\) 是数据结构单点操作复杂度。

而由于重边在儿子中的唯一性,所以所有的重边形成了若干条,不妨称之重链

一条链上包含多条重链。由于轻重边的交替排布,重链的数量级也是 \(\log n\) 的。考虑到大部分数据结构维护的是下标连续的区间,于是这里有一个小 trick,DFS 求 dfn 时,优先走重边,这样重链上的下标就连续了,所以我们只要做一次数据结构的区间操作即可维护重边上的信息,记 \(top(u)\) 为包含 \(u\) 的重链的链头,则每次跳 \(u\to fa(top(u))\) 即可一次跳过一条重链加一条轻边,时间复杂度 \(O(F'(n)\log n)\),\(F'(n)\) 为数据结构区间操作复杂度,对于线段树这种的,\(F'(n)\doteq F(n)\)。

综上对于一条链的操作,复杂度就为 \(O(F(n)\log n)\)。

例题:

给你一棵带边权树,支持下列操作:

  • 查询子树和
  • 查询 \(u,v\) 路径和
  • 修改子树边权
  • 修改路径边权

先 DFS 出 dfn,然后操作 1、3 对应了一段连续的区间,直接线段树区间操作即可;操作 2、4 首先将路径拆成两条链 \((u,\text{lca}),(\text{lca},v)\),然后分别剖即可。时间复杂度分别为 \(O(\log n),O(\log^2 n)\)。

实现

第一遍 DFS 求出以下信息:

  • \(dep(u)\):\(u\) 的深度;
  • \(siz(u)\):\(u\) 子树大小;
  • \(son(u)\):\(u\) 的重儿子;
  • \(fa(u)\):\(u\) 的父亲。
void dfs1(int u,int Fa){
    dep[u]=dep[Fa]+1;
    fa[u]=Fa;
    int mxson,mxsiz=0;
    for(edge v:e[u]){
        if(v.v!=fa){
            dfs1(v.v,u);
            siz[u]+=siz[v.v];
            if(siz[v.v]>mxsiz)
                mxsiz=siz[v.v],mzson=v.v;
        }
    }
    son[u]=mxson;
}

第二遍 DFS 求出以下信息:

  • \(top(u)\):包含 \(u\) 的重链链头;
  • \(dfn(u)\):\(u\) 的 dfn;
  • \(idx(u)\):\(u\) 的 dfn 逆向索引。
void dfs2(int u,int t){
    top[u]=t;
    dfn[u]=++dfncnt;
    idx[dfncnt]=u;
    if(top[son[u]]!=t) dfs2(son[u],son[u]);
    else dfs2(son[u],t);
    for(edge v:e[u]){
        if(v.v!=fa[u]&&v.v!=son[u]){
            dfs2(v.v,v.v); // 轻儿子肯定不在 u 的重链上
        }
    }
}

修改路径:

void modify_chain(int u,int v,int x){
    int fu=u,fv=v;
    while(top[fu]!=top[fv]){
        if(dep[top[fu]]<dep[top[fv]]) swap(fu,fv); 
        modify(1,1,n,dfn[top[fu]],dfn[fu],x);
        fu=fa[top[fu]]; // 每次选深的跳直到到同一重链上
    }
    if(dep[fu]>dep[fv]) swap(fu,fv);
    modify(1,1,dfn[fu],dfn[fv],x);
}

查询路径:

int query_chain(int u,int v,int x){
    int fu=u,fv=v,res=0;
    while(top[fu]!=top[fv]){
        if(dep[top[fu]]<dep[top[fv]]) swap(fu,fv); 
        res+=query(1,1,n,dfn[top[fu]],dfn[fu],x);
        fu=fa[top[fu]]; // 每次选深的跳直到到同一重链上
    }
    if(dep[fu]>dep[fv]) swap(fu,fv);
    res+=query(1,1,dfn[fu],dfn[fv],x);
    return res;
}

特殊的,如果信息在点上,则可以把信息转移到边 \((u,fa(u))\) 上。

标签:fu,剖分,int,top,树链,dfn,fv,启发式,重链
From: https://www.cnblogs.com/DEV3937/p/18458903

相关文章

  • 启发式合并
    入门例题[ABC329F]ColoredBall。题意给定\(N\)个盒子,每个盒子里面有一个颜色为\(C_i\)的小球。有\(Q\)次操作,每次操作将第\(a_i\)个盒子中的球都放到第\(b_i\)个盒子里面,你需要在每次操作后输出当前操作结束后第\(b_i\)个盒子里面有多少个不同颜色的小球。如......
  • 树链剖分
    考一遍,学一遍,忘一遍这里是重链剖分。两个dfs,第一个找重儿子,第二个找重链顶和dfn(注意要优先对重儿子dfs来保证同一条重链上的dfs序连续)查询和维护时一个一个跳重链顶端,时间复杂度O(nlogn)。常和线段树配套使用。模板#include<bits/stdc++.h>#definelllonglong#defineli......
  • [OI] 树链剖分
    学的时候比较朦胧,现在不朦胧了,所以写一下讲解重儿子:一个节点的子树大小最大的儿子轻儿子:非重儿子重链:节点->重儿子->重儿子..这样的链AbeautifulTree蓝线为重链可以发现,树上的所有节点一定属于且仅属于一个重链首先要知道如何找重链这很简单,可以通过一遍DFS......
  • 开普勒优化算法:一种开普勒行星运动定律的元启发式算法
    目录1.摘要2.算法原理3.结果展示4.参考文献5.代码获取1.摘要这项研究介绍了开普勒优化算法(KOA),这是一种基于物理的新元启发式算法,灵感来源于开普勒行星运动定律。KOA通过模拟行星的位置和速度来寻找优化问题的解决方案,其中每个行星代表一个候选解,这些候选解会根据......
  • 什么是启发式过滤(Heuristic Filtering)?
    定义启发式过滤是一种技术方法,利用解决问题的技术和算法来识别数据中的模式、趋势或特征。这种方法通常涉及使用预测分析和近似方法,以便快速做出决策或对信息进行分类。启发式过滤通常应用于反垃圾邮件软件、防病毒程序和人工智能等领域,以有效检测垃圾邮件、恶意软件或......
  • [学习笔记]树链剖分(简易版) 及其LCA
    树链剖分先讲解一下一些基础定义(都是在树上)重儿子:一个节点中所有儿子中子树大小最大的一个儿子(每个节点最多有一个重儿子)轻儿子:一个节点除重儿子外所有的节点重链:若干个重儿子组成的链链顶:一条链中深度最小的节点以下图为例子(红色连续线段为重链)对于节......
  • 树链剖分
    由于是在树上搞的ds所以考察数据结构本身性质偏多,需大力注重细节。思想考虑将一颗树的链划分成若干个区间进行维护。这种划分方式叫做剖分。约束一颗有根树(有时要求换根但不是真正换根)每个点恰好包含在一条剖出的链中(若被多条链同时包含则需要同时维护多条链,修改多余......
  • 树链剖分
    原理:将一棵树剖分成一条条的链,从而降低时间复杂度首先会一个线段树,书完成剖分后,用来维护每一条的信息。#include<bits/stdc++.h>typedefintintt;#defineintlonglong#definelck<<1#definerck<<1|1constintM=2e6+10;usingnamespacestd;intn,m,ans......
  • 【转载】启发式合并
    https://zhuanlan.zhihu.com/p/560661911数据结构学习笔记(8)启发式合并启发式合并是用来解决子树中的统计问题。在codeforces上叫做dsuontree(树上启发式合并)。这里我们主要是来讲在树上进行启发式合并。实际上之前我有讲过启发式合并严格鸽:启发式合并看似暴力实则很快的......
  • 树链剖分
    树链剖分的思想及能解决的问题树链剖分用于将树分割成若干条链的形式,以维护树上路径的信息。具体来说,将整棵树剖分为若干条链,使它组合成线性结构,然后用其他的数据结构维护信息。树链剖分(树剖/链剖)有多种形式,如重链剖分,长链剖分和用于Link/cutTree的剖分(有时被称作「实链剖......