当前位置: 首页 > news >正文

树链剖分(重链剖分)

树链剖分的核心思想就是将一棵树剖分成一条一条的链

因为树不好处理 但链比较好处理

为了学会它 我们先要学会树上dfs(深度优先搜索) 然后就没了(雾)

Because 树链剖分需要用到两个dfs

哦对了 我们还要了解以下的知识点

1.子树大小

就是一个节点的子树的节点个数(包括它自己)

2.重儿子

一个节点的所有儿子中子树大小最大的儿子

3.轻儿子

除了重儿子之外的儿子(包括根节点)

4.重边

两个相邻的重儿子连成的边

5.重链

重边连成的链(开头是轻儿子)

OK,了解完了

启动!

dfs1:

目标是求出以下数据

1: 节点的父节点 fa[x]

2: 节点的深度 也就是它到根节点的距离 deep[x]

3: 节点的重儿子 son[x]

4: 节点的子树大小 si[x]

思路十分的简单 直接上代码

void dfs1(int x)
{deep[x]=deep[fa[x]]+1;si[x]=1;//它自己也是 for(int i=0;i<a[x].size();i++){if(a[x][i]==fa[x]) continue;fa[a[x][i]]=x;dfs1(a[x][i]);si[x]+=si[a[x][i]];if(si[a[x][i]]>si[son[x]]) son[x]=a[x][i];}
}

dfs2:

目标是求出以下数据

1: 节点的所在的重链的顶端节点 top[x]

2: 节点的新编号 id[x]

3: 节点的新编号的值 b[x]

其中 新编号的原则是先走重儿子 再走轻儿子(理由下次一定)

比如

懂了吗? 我相信你肯定懂了

思路也十分的简单 直接上代码

​
void dfs2(int x,int topf)
{id[x]=++cnt;top[x]=topf;b[cnt]=w[x];if(!son[x]) return;dfs2(son[x],topf);for(int i=0;i<a[x].size();i++){if(a[x][i]==fa[x]||a[x][i]==son[x]){continue;}dfs2(a[x][i],a[x][i]);}
}​

我们发现先走重儿子的话 就可以让重链上的点的新编号是连续的 也就形成了一条链

至此 树链剖分就结束了

那你一定很想问 这玩意有什么用呢?

题目

我们结合题目来看(其实是模板)

前置芝士:线段树

让我们处理4种操作

我们先来看操作3和操作4

通过观察图片可以看出

一个节点的子树的编号肯定是连续的

设子树的根是x

左端点是的编号是x 右端点的编号是x+si[x]-1

所以3操作就是将xx+si[x]-1加上z

4操作就是求出xx+si[x]-1的值的和

即区间修改 ,区间查询

我们当然会想到线段树啦

直接拿下

之后我们来看1,2操作

先来看一看图

比如我们要从 7 到 4

我们可以先判断它俩的top是不是一个东西 发现不是 

发现4的深度比7小 这怎么可以呢?

我们交换一下 变成从 4 到 7

然后把4跳到它的top的父节点

也就是0(1的父节点是0)

并加上 1 到 4 的值 可以用线段树处理

之后在循环上述操作

直到两者的top相等

上代码

int getsum1(int x,int y)
{int sum=0;while(top[x]!=top[y]){if(deep[top[x]]<deep[top[y]]){swap(x,y);}sum=sum+query(1,1,n,id[top[x]],id[x]);x=fa[top[x]];}if(deep[x]>deep[y]){swap(x,y);}sum+=query(1,1,n,id[x],id[y]);return sum;
}

修改也差不多 直接上代码

void addsum1(int x,int y,int z)
{while(top[x]!=top[y]){if(deep[top[x]]<deep[top[y]]){swap(x,y);}add(1,1,n,id[top[x]],id[x],z);x=fa[top[x]];}if(deep[x]>deep[y]){swap(x,y);}add(1,1,n,id[x],id[y],z);
}

完整版:

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,cnt,m,r,mod;
int b[110000],top[110000],w[110000],deep[110000],id[110000],fa[110000],si[110000],son[110000],tr[410000],tag[410000];
vector<int>a[110000];
void dfs1(int x)
{deep[x]=deep[fa[x]]+1;si[x]=1;//它自己也是 for(int i=0;i<a[x].size();i++){if(a[x][i]==fa[x]) continue;fa[a[x][i]]=x;dfs1(a[x][i]);si[x]+=si[a[x][i]];if(si[a[x][i]]>si[son[x]]) son[x]=a[x][i];}
}
void dfs2(int x,int topf)
{id[x]=++cnt;top[x]=topf;b[cnt]=w[x];if(!son[x]) return;dfs2(son[x],topf);for(int i=0;i<a[x].size();i++){if(a[x][i]==fa[x]||a[x][i]==son[x]){continue;}dfs2(a[x][i],a[x][i]);}
}
void build(int k,int l,int r)
{if(l==r){tr[k]=b[l];return;}int mid=(l+r)>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);tr[k]=tr[k<<1]+tr[k<<1|1];
}
void push_down(int k,int l,int r)
{if(tag[k]){int mid=(l+r)>>1;tag[k<<1]+=tag[k];tag[k<<1|1]+=tag[k];tr[k<<1]+=(mid-l+1)*tag[k];tr[k<<1|1]+=(r-mid)*tag[k];tag[k]=0;}
}
void add(int k,int l,int r,int q,int p,int d)
{if(q<=l&&p>=r){tag[k]+=d;tr[k]+=(r-l+1)*d;return;}push_down(k,l,r);int mid=(l+r)>>1;if(mid>=q){add(k<<1,l,mid,q,p,d);}if(mid<p){add(k<<1|1,mid+1,r,q,p,d);}tr[k]=(tr[k<<1]+tr[k<<1|1]);
}
int query(int k,int l,int r,int q,int p)
{if(q<=l&&p>=r){return tr[k];}push_down(k,l,r);int mid=(l+r)>>1,ssum=0;if(mid>=q){ssum+=query(k<<1,l,mid,q,p);}if(mid<p){ssum+=query(k<<1|1,mid+1,r,q,p);}return ssum;
}
int getsum1(int x,int y)
{int sum=0;while(top[x]!=top[y]){if(deep[top[x]]<deep[top[y]]){swap(x,y);}sum=sum+query(1,1,n,id[top[x]],id[x]);x=fa[top[x]];}if(deep[x]>deep[y]){swap(x,y);}sum+=query(1,1,n,id[x],id[y]);return sum;
}
int getsum2(int x)
{return query(1,1,n,id[x],id[x]+si[x]-1);
}
void addsum1(int x,int y,int z)
{while(top[x]!=top[y]){if(deep[top[x]]<deep[top[y]]){swap(x,y);}add(1,1,n,id[top[x]],id[x],z);x=fa[top[x]];}if(deep[x]>deep[y]){swap(x,y);}add(1,1,n,id[x],id[y],z);
}
void addsum2(int x,int y)
{add(1,1,n,id[x],id[x]+si[x]-1,y);
}
signed main()
{scanf("%lld%lld%lld%lld",&n,&m,&r,&mod);for(int i=1;i<=n;i++){scanf("%lld",&w[i]);}for(int i=1;i<n;i++){int x,y;scanf("%lld%lld",&x,&y);a[x].push_back(y);a[y].push_back(x);}dfs1(r);dfs2(r,r);build(1,1,n);while(m--){int op;scanf("%lld",&op);if(op==1){int x,y,z;scanf("%lld%lld%lld",&x,&y,&z);addsum1(x,y,z);}if(op==2){int x,y;scanf("%lld%lld",&x,&y);printf("%lld\n",getsum1(x,y)%mod);}if(op==3){int x,y;scanf("%lld%lld",&x,&y);addsum2(x,y);}if(op==4){int x;scanf("%lld",&x);printf("%lld\n",getsum2(x)%mod);}}return 0;
}

相关文章:

树链剖分(重链剖分)

树链剖分的核心思想就是将一棵树剖分成一条一条的链 因为树不好处理 但链比较好处理 为了学会它 我们先要学会树上dfs&#xff08;深度优先搜索&#xff09; 然后就没了&#xff08;雾&#xff09; Because 树链剖分需要用到两个dfs 哦对了 我们还要了解以下的知识点 1.子…...

幻读是什么?用什么隔离级别可以防止幻读?

幻读是什么&#xff1f; 幻读&#xff08;Phantom Read&#xff09; 是数据库事务中的一种现象&#xff0c;指的是在一个事务中&#xff0c;当执行两次相同的查询时&#xff0c;第二次查询返回的结果集包含了第一次查询中不存在的行&#xff0c;或者第一次查询中存在的行在第二…...

[Unity Demo]从零开始制作空洞骑士Hollow Knight第二十集:制作专门渲染HUD的相机HUD Camera和画布HUD Canvas

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、制作HUD Camera以及让两个相机同时渲染屏幕二、制作HUD Canvas 1.制作法力条Soul Orb引入库2.制作生命条Health读入数据3.制作吉欧统计数Geo Counter4.制作…...

智能安全配电装置在高校实验室中的应用

​ 摘要&#xff1a;高校实验室是科研人员进行科学研究和实验的场所&#xff0c;通常会涉及到大量的仪器设备和电气设备。电气设备的使用不当或者维护不周可能会引发火灾事故。本文将以一起实验室电气火灾事故为例&#xff0c;对事故原因、危害程度以及防范措施进行分析和总结…...

网络安全等级保护测评机构管理办法(全文)

网络安全等级保护测评机构管理办法(公信安〔2018〕765号) 第一章 总则 第一条 为加强网络安全等级保护测评机构&#xff08;以下简称“测评机构”&#xff09;管理&#xff0c;规范测评行为&#xff0c;提高等级测评能力和服务水平&#xff0c;根据《中华人民共和国网络安全法…...

Flutter:shared_preferences数据存储,数据持久化,token等信息存储

官方示例&#xff1a;简单调用 // 初始化示例 final SharedPreferences prefs await SharedPreferences.getInstance(); // 存int await prefs.setInt(counter, 10); // 存bool await prefs.setBool(repeat, true); // 存double await prefs.setDouble(decimal, 1.5); // 存st…...

FileProvider高版本使用,跨进程传输文件

高版本的android对文件权限的管控抓的很严格,理论上两个应用之间的文件传递现在都应该是用FileProvider去实现,这篇博客来一起了解下它的实现原理。 首先我们要明确一点,FileProvider就是一个ContentProvider,所以需要在AndroidManifest.xml里面对它进行声明: <provideran…...

python学习记录18

1 函数的定义 python中的函数指使用某个定义好的名字指代一段完整的代码&#xff0c;在使用名字时可以直接调用整个代码&#xff0c;这个名字叫做函数名。利用函数可以达到编写一次即可多次调用的操作&#xff0c;从而减少代码量。 函数分为内置函数与自定义函数。内置函数例…...

云原生之k8s服务管理

文章目录 服务管理Service服务原理ClusterIP服务 对外发布应用服务类型NodePort服务Ingress安装配置Ingress规则 Dashboard概述 认证和授权ServiceAccount用户概述创建ServiceAccount 权限管理角色与授权 服务管理 Service 服务原理 容器化带来的问题 自动调度&#xff1a;…...

redis工程实战介绍(含面试题)

文章目录 redis单线程VS多线程面试题**redis是多线程还是单线程,为什么是单线程****聊聊redis的多线程特性和IO多路复用****io多路复用模型****redis如此快的原因** BigKey大批量插入数据测试数据key面试题海量数据里查询某一固定前缀的key如果生产上限值keys * &#xff0c;fl…...

再次讨论下孤注一掷

在孤注一掷中的黑客技术里面&#xff0c;简单介绍了电影孤注一掷中用的一些"黑科技"&#xff0c;这里继续讨论下&#xff0c;抛弃这些黑科技&#xff0c;即使在绝对公平的情况下&#xff0c;你也一样赢不了赌场 相对论有一个假设就是光速不变&#xff0c;这里也有个…...

LeetCode46.全排列

LeetCode刷题记录 文章目录 &#x1f4dc;题目描述&#x1f4a1;解题思路⌨C代码 &#x1f4dc;题目描述 给定一个不含重复数字的数组 nums &#xff0c;返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 示例1 输入&#xff1a;nums [1,2,3] 输出&#xff1a;[[1,2,…...

蓝桥杯-洛谷刷题-day4(C++)

目录 1.高精度乘法 i.P1303 A*B Problem高精度乘法 2.P4924 [1007] 魔法少女小Scarlet i.题目 ii.代码 3.二维数组 i.二维数组的建立 ii.备份 iii.二维数组的转动 4.指令的及时处理 1.高精度乘法 即&#xff0c;将每一位变为数组中的一位&#xff0c;并在数组中以倒序排列&a…...

c++总复习

1. C 中的移动语义及其作用 定义 移动语义是 C 11 引入的一种重要特性&#xff0c;它用于优化对象的资源管理&#xff0c;特别是在涉及对象所有权转移的场景中。传统的 C 语义在对象赋值或传递给函数时&#xff0c;通常会进行拷贝操作&#xff0c;即创建源对象的一个完整副本&…...

设计模式之策略模式-工作实战总结与实现

文章目录 应用场景存在问题解决方案继续延伸 应用场景 假设有这样的业务场景&#xff0c;大数据系统把文件推送过来&#xff0c;根据不同类型采取不同的解析方式。多数的小伙伴就会写出以下的代码&#xff1a; public class Question {public static void main(String[] args…...

E - 11/22 Subsequence题解

文章目录 大致思路代码 大致思路 预处理: 用pos1, pos2, posls 分别记录 1 1 1, 2 2 2 , / / / 在字符串中的『位置』 用cum1 和 cum2 分别存储了 1 1 1 和 2 2 2 的前缀和&#xff0c;这样可以快速获取任意区间内的 1 1 1 和 2 2 2 的『数量』 查询处理: 对于每个查询…...

PyPI 攻击:ChatGPT、Claude 模仿者通过 Python 库传播 JarkaStealer

《Java代码审计》http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247484219&idx1&sn73564e316a4c9794019f15dd6b3ba9f6&chksmc0e47a67f793f371e9f6a4fbc06e7929cb1480b7320fae34c32563307df3a28aca49d1a4addd&scene21#wechat_redirect 《Web安全》h…...

单片机学习笔记 9. 8×8LED点阵屏

更多单片机学习笔记&#xff1a;单片机学习笔记 1. 点亮一个LED灯单片机学习笔记 2. LED灯闪烁单片机学习笔记 3. LED灯流水灯单片机学习笔记 4. 蜂鸣器滴~滴~滴~单片机学习笔记 5. 数码管静态显示单片机学习笔记 6. 数码管动态显示单片机学习笔记 7. 独立键盘单片机学习笔记 8…...

【大模型-智能体】AutoGen Studio测试和导出工作流程

1. 测试工作流程 AutoGen Studio允许用户针对任务交互式地测试工作流程&#xff0c;并审查由此产生的成果物&#xff08;如图像、代码和文档&#xff09;。此外用户还可以查看Agent工作流程在处理任务时的“内心独白”&#xff0c;并查看诸如运行成本&#xff08;如回合数、令牌…...

【Linux】-学习笔记04

第十二章、磁盘管理 1.查看磁盘空间使用量 1.1df命令 作用&#xff1a; 列出文件系统的磁盘空间占用情况 df&#xff0c;disk free&#xff0c;通过文件系统来快速获取空间大小的信息&#xff0c;当我们删除一个文件的时候&#xff0c;这个文件 不是马上就在文件系统当中消…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现

目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

Prompt Tuning、P-Tuning、Prefix Tuning的区别

一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...

MongoDB学习和应用(高效的非关系型数据库)

一丶 MongoDB简介 对于社交类软件的功能&#xff0c;我们需要对它的功能特点进行分析&#xff1a; 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具&#xff1a; mysql&#xff1a;关系型数据库&am…...

渲染学进阶内容——模型

最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...

css3笔记 (1) 自用

outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size&#xff1a;0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格&#xff…...

【JVM面试篇】高频八股汇总——类加载和类加载器

目录 1. 讲一下类加载过程&#xff1f; 2. Java创建对象的过程&#xff1f; 3. 对象的生命周期&#xff1f; 4. 类加载器有哪些&#xff1f; 5. 双亲委派模型的作用&#xff08;好处&#xff09;&#xff1f; 6. 讲一下类的加载和双亲委派原则&#xff1f; 7. 双亲委派模…...

LLMs 系列实操科普(1)

写在前面&#xff1a; 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容&#xff0c;原视频时长 ~130 分钟&#xff0c;以实操演示主流的一些 LLMs 的使用&#xff0c;由于涉及到实操&#xff0c;实际上并不适合以文字整理&#xff0c;但还是决定尽量整理一份笔…...

「全栈技术解析」推客小程序系统开发:从架构设计到裂变增长的完整解决方案

在移动互联网营销竞争白热化的当下&#xff0c;推客小程序系统凭借其裂变传播、精准营销等特性&#xff0c;成为企业抢占市场的利器。本文将深度解析推客小程序系统开发的核心技术与实现路径&#xff0c;助力开发者打造具有市场竞争力的营销工具。​ 一、系统核心功能架构&…...

《Docker》架构

文章目录 架构模式单机架构应用数据分离架构应用服务器集群架构读写分离/主从分离架构冷热分离架构垂直分库架构微服务架构容器编排架构什么是容器&#xff0c;docker&#xff0c;镜像&#xff0c;k8s 架构模式 单机架构 单机架构其实就是应用服务器和单机服务器都部署在同一…...

篇章二 论坛系统——系统设计

目录 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 1. 数据库设计 1.1 数据库名: forum db 1.2 表的设计 1.3 编写SQL 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 通过需求分析获得概念类并结合业务实现过程中的技术需要&#x…...