毛毛虫剖分

news/2025/3/13 8:34:14/文章来源:https://www.cnblogs.com/Richardwhr/p/18768685

科技,用来处理小半径邻域和子树,链等的结合题型。

本质是对树重编号,将询问的区间划分成尽可能少的区间。

BDFS 序:

这个可以处理同时存在邻域和子树查询的信息。

假设要求 \(k\) 邻域,那么单次操作复杂度就是 \(O(k \log n)\)

重编号设计:

重编号思想:

容易发现 bfs 序满足子树任意的 \(k\) 邻域都是一个区间,而 \(dfs\) 序满足子树是一个区间。

此时我们不需要很多的 \(k\),但是要查一整颗子树。

考虑将二者结合,我们将原来的 \(dfs\) 序中, \(u\) 的位置替换为他的子树 \(k\) 邻域集合的 \(bfs\) 序。

此时满足所有 \(k' \le k\) 邻域都是一个区间,而 \(u\) 的所有后代替换的位置都在 \(u\) 之后,说明 \(k' >k\) 的邻域子树一起构成一个区间。

重编号流程:

假设最大要求查询 \(k\) 邻域。

  • 先将根节点的 \(k-1\) 及以下邻域按照 bfs 序加入编号序列。

  • 定义 \(son_{u,i}\) 表示 \(u\) 子树中 \(i\) 邻域的点的集合,按照 \(dfs\) 序排序。按 dfs 顺序访问到 \(u\) 的时候,依次将 \(son_{u,k}\) 中的元素加入编号序列。

点击查看代码
for(int i=0;i<M-1;i++) for(auto v:son[1][i]) dfn[v]=++ts;void Dfs(int u)
{Bg[u]=ts+1;for(auto v:son[u][M-1]) dfn[v]=++ts;for(auto v:e[u]){if(v==fa[u]) continue;Dfs(v);}En[u]=ts;
}

\([Bg_u, En_u]\) 是所有大于 \(k\) 的邻域子树编号区间。

询问流程

  • 邻域操作:

    枚举和邻域中的点的 \(Lca\),注意如果要求只算 \(k\) 邻域而不算更小的,需要一些容斥:

    • \(K\) 以内邻域:

      点击查看代码
      int Subk(int u,int k,int c)
      {return modify(1,L[u][k],R[u][k],c);
      }int calc1(int u,int k,int c)
      {int mx=-Inf;for(int i=k;i>=0;i--){mx=max(mx,Subk(u,i,c));if(fa[u] && i) mx=max(mx,Subk(u,i-1,c));if(fa[u]) u=fa[u];}	return mx;
      }
      
    • \(K\) 邻域:

      直接对整体容斥:

      点击查看代码
      int calc2(int u,int k,int c)
      {if(k) calc1(u,k-1,-Inf);int res=calc1(u,k,c);if(k) calc1(u,k-1,Inf-c);return res;
      }
      

      这是其中一种写法,基于全局的容斥,还有一种是可以做不支持容斥的,直接对区间做容斥:

      点击查看代码
      int Calc1(int u,int k,int c)
      {int res=-Inf;modify(1,L[u][k],R[u][k],c,res);int d=1;while(fa[u] && d<=k){int p=fa[u];int _d=k-d;if(_d==0){modify(1,dfn[p],dfn[p],c,res);	}else{int l1=L[p][_d],r1=R[p][_d],l2=L[u][_d-1],r2=R[u][_d-1];if(l2<=r2){modify(1,l1,l2-1,c,res),modify(1,r2+1,r1,c,res);}else{modify(1,l1,r1,c,res);}	}u=p,d++;}return res;
      }
      
  • 子树操作:

    拆分为子树内 \(k\) 以内邻域以及 \(k\) 以外子树即可:

    点击查看代码
    int Sub(int u,int c)
    {int res=-Inf;for(int i=0;i<M;i++) res=max(res,Max(u,i,c));res=max(res,modify(1,L[u][M],R[u][M],c));return res;
    }
    

例题:

DMY 邻域查询

K-毛毛虫剖分

这个是上面 bdfs 序的一个树剖加强,增加了链邻域的操作,更为复杂。

本质是我们将树剖成若干重链,然后按照 dfs 的顺序去遍历这些链(不一定要是严格的,可以看成轻边链接的都是父子关系)。

然后每条链去做刚刚那个 bdfs 重编号。

此时我们注意到这样做满足如下性质:

  • 每个点,除去重儿子方向的子树,满足 \(k\) 邻域重编号构成区间,\(k\) 以外子树内邻域重编号为区间。

  • 对于每条重链,除去链顶端的 \(k\) 个点,其余所有点的 \(k\) 邻域重编号构成一个区间。

因此对于子树方向的查询相比之前,只需要多讨论重儿子即可。

对于链上的询问,相比树剖需要特殊考虑链顶的 \(k\) 个节点。

代码可以这样写:

点击查看代码
void renum(int u,int d,int p,int &l,int &r)//找到距离 u 恰好为 d 的点
{if(!d){if(!dfn[u]) dfn[u]=++ts,seq[ts]=u;l=min(l,dfn[u]),r=max(r,dfn[u]);return;}for(auto v:e[u]){if(v==fa[u] || (!p && v==son[u])) continue;renum(v,d-1,u,l,r);}
}void reorder(int u)
{int v;for(int i=0;i<=M;i++)//bfs序依次加入{v=u;while(v){int bg=ts;renum(v,i,0,L[v][i],R[v][i]);if(L[v][i]>R[v][i]) L[v][i]=bg+1,R[v][i]=bg;//如果为空,设置为 [last+1,last],保证和下面的构成连续区间v=son[v];}}v=u;while(v)//按dfs序处理重链{Bg[v]=ts+1;// k 以外邻域起始编号for(auto z:e[v]){if(z==fa[v] || z==son[v]) continue;reorder(z);}v=son[v];}v=u;while(v) En[v]=ts,v=son[v];
}

基础操作:

  • 对于某个点 \(u\),它的子树 \(k\) 邻域操作:

    每次将这一层的轻儿子整体做完后,递归到重儿子处理,由于 \(k\) 很小,次数不会超过 \(k\) 次,复杂度 \(O(k \log n)\)

    点击查看代码
    void Suboperate(int u,int k)
    {for(int i=k;i>=0;i--){operate(L[u][i],R[u][i],c);u=son[u];}
    }
    

    有了这个我们就可以较为方便的做某个点的 \(k\) 以内邻域操作了:

    点击查看代码
    void Neoperate(int u,int k)
    {for(int i=k;i>=0 ;i--){Suboperate(u,i);if(fa[u] && i) Suboperate(u,i-1);if(fa[u]) u=fa[u];}
    }
    
  • 对于条重链片段 \(u \rightarrow v\),操作链方向子树内,链的 \(k\) 以内邻域,不包括连顶 \(k-1\) 内邻域。

    为啥要不包含链顶?因为如果他的上面没有链了,说明到达了 LCA,直接补上即可。否则他的上面一定有链,在这个链执行完当前操作后就会把他的链顶部分给算上。

    相当于所有点同时做上面的操作,每次只算恰好 \(k\) 邻域的部分,链顶前几个单独拎出来加,后面的 \(k\) 邻域轻儿子部分构成了一个完整区间。然后所有点向下平移,递归处理。单词递归复杂度 \(O(k \log n)\),总复杂度 \(O(k^2 \log n)\)

    点击查看代码
    void Linkoperate(int u,int v,int k)
    {if(!u) return;if(k) Linkoperate(son[u],(son[v])?son[v]:v,k-1);for(int i=0;i<M && u!=v;i++){operate(L[u][k],R[u][k]);u=son[u];}operate(L[u][k],R[v][k]);
    }
    

进阶操作(子树,链邻域)

  • 子树:

    根据重编号的性质有:对于每个点,除去重儿子方向,所有 \(k\) 以外邻域,子树内部分构成一个完整区间。这部分直接做就行。

    然后补上重儿子方向的,可以使用刚才的链操作维护。

    这样最后还剩下 \(u\)\(k-1\) 以内邻域,子树内部分,每一层做基础操作 1 就行。

    总复杂度 \(O(\log n+ k^2 \log n+k^2 \log n)=O(k^2 \log n)\)

    点击查看代码
    void operate_sub(int u)
    {operate(Bg[u],En[u]);Linkoperate(u,ed[u],M);//ed_u 是 u 所在重链的末尾 for(int i=0;i<M;i++) Suboperate(u,i);
    }
    
  • 链邻域:

    拆出来每条重链分别做,注意不要操作到 LCA。那这样最后就剩下了 \(u\)\(k\) 邻域以内部分了。

    点击查看代码
    void operate_link(int u,int v,int k)
    {while(top[u]!=top[v]){if(dep[top[u]]<dep[top[v]]) swap(u,v);Linkoperate(top[u],u,k);u=fa[top[u]];}if(dep[u]>dep[v]) swap(u,v);if(u!=v) Linkoperate(son[u],v,k);Neoperate(u,k,c);
    }
    

例题

[2023集训队互测] 数据结构

[2024集训队互测] 数据结构


网上讲解相当少,而且大部分都是 1-毛毛虫剖分,只能自己看这两道例题别人的代码理解,非常困难,肝了一天才差不多明白,代码理解了之后其实并不难写。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/898001.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DeepSeek 十大提问公式 | AI 通用

AI 辅助教学案例 03,收集整理并记录现有的比较火爆的国产 AI 在教育教学中的使用案例,以期和各位同仁、莘莘学子共同进步。[第三期]前情概要 DeepSeek 十大提问公式

指在PCB上的导线与焊盘或过孔之间的连接处添加的一个三角形或弧形的填充物,形状类似于泪滴,因此得名。

1. PCB Layout 步骤生成PCB确定PCB layout规范绘制板框尺寸布局 布局规范:按电气性能合理分区,一般分为:数字电路区(即怕干扰、又产生干扰)、模拟电路区(怕干扰)、功率驱动区(干扰源); 完成同一功能的电路,应尽量靠近放置,并调整各元器件以保证连线最为简洁; 对于质…

打造出更加智能、便捷的学习与咨询体验。

扣子(coze.cn)是一款用来开发新一代 AI Chat Bot 的应用编辑平台,无论你是否有编程基础,都可以通过这个平台来快速创建各种类型的 Chat Bot,并将其发布到各类社交平台和通讯软件上!2月1日,扣子国内版已经正式上线啦~赶快来体验一下吧!一转眼,ChatGPT已经在AI界炙手可热…

数据库通常使用索引来提高业务查询的速度。本文将深入介绍GaussDB中最常用的两种索引

本文分享自华为云社区《【GaussTech技术专栏】GaussDB的BTree索引和UBTree索引》,作者:GaussDB 数据库。 1. 简介 数据库通常使用索引来提高业务查询的速度。本文将深入介绍GaussDB中最常用的两种索引:BTree索引和UBTree索引。我们将重点解读BTree索引和UBTree索引的存储结构…

在OSG中,对于一些效果未被选中或者包含等业务,需要半透明效果来实现。

在OSG中,对于一些效果未被选中或者包含等业务,需要半透明效果来实现。  本篇描述OSG的半透明实现方式。 Demo 透明功能概述透明效果在三维场景中扮演着重要角色,它能够模拟玻璃、水体、烟雾等自然现象,增加场景的层次感和真实感。然而,透明效果的实现并非易事,它涉及到…

前端助手是一个基于腾讯元器的智能体

在当今智能技术蓬勃发展的时代,开发一个属于自己的专属机器人已经变得非常容易。在本文中,我们将探讨如何通过腾讯元器来构建一个前端助手智能体,以帮助我们解决前端开发过程中的问题。通过一个简单的示例,我们将模拟我们在遇到问题时如何寻找解决方案的过程。 前端助手 前…

翻译行业随机抽取Excel数据并在处理后整合为一个文件

本文介绍基于Python语言,针对一个文件夹下大量的Excel表格文件,基于其中每一个文件,随机从其中选取一部分数据,并将全部文件中随机获取的数据合并为一个新的Excel表格文件的方法。首先,我们来明确一下本文的具体需求。现有一个文件夹,其中有大量的Excel表格文件(在本文中…

模型还会为大家生成一份总结和建议,以便更好地梳理和应用所学内容。

今天我们将实现一个学习英语的智能助手。回想一下大家小时候的英语学习经历,不知道你们都用过什么课外英语教材。对我来说,小时候在英语补习班上最常接触的就是《新概念英语》。 因此,我们决定直接利用《新概念英语》的教材,构建一个专属于你的英语学习小助手。这个助手将迅…

采用异常重试实现故障恢复

网络延迟或者抖动 服务器资源不足(CPU、内存走高、连接池满) 服务器故障 符合某些特定条件下的服务程序bug(大都非必现)2 系统稳定性等级划分 大部分服务容忍低频、偶发的5xx错误,并使用可用性级别来衡量系统的健壮性,级别系数越高,健壮性越好,如下:等级描述故障时长(…

核心思想是通过机器学习模型对用户的意图进行分类

市场规模 智能客服的市场规模非常庞大,且不断增长。其主要技术目标是实现对高频率、简单问题的自动处理,以大幅度减少人工客服的负担。这种自动化处理可以显著提升服务效率,降低企业成本,同时保证基础问题的快速响应。然而,对于复杂和疑难问题,人工客服仍然是不可或缺的,…

为未来的数据存储技术发展奠定了基础。

观历史 正如俗话所说的,“好记性不如烂笔头”,现代人们常依赖纸质记录来保存和维护数据。实际上,数据存储的需求自人类诞生之初便开始显现。让我们从数据存储的角度出发,深入探讨数据库的发展历程,以了解数据存储如何随时间演变和进步。 在我看来,数据库不仅仅是一种软件…

中有大量的Excel表格文件(在本文中我们就以.csv格式的文件为例

本文介绍基于Python语言,针对一个文件夹下大量的Excel表格文件,基于其中每一个文件,随机从其中选取一部分数据,并将全部文件中随机获取的数据合并为一个新的Excel表格文件的方法。首先,我们来明确一下本文的具体需求。现有一个文件夹,其中有大量的Excel表格文件(在本文中…