树状数组与线段树<2>——线段树初步

这个系列终于更新了(主要因为树状数组初步比较成功)

话不多说,切入正题。

什么是线段树?

线段树是一种支持单点修改区间查询(树状数组也行) and 区间修改单点查询(树状数组不行) and 区间修改区间查询(树状数组更不行)的高级数据结构,相当于树状数组plus版。

——该图片来自百度百科

建树

我们先来看看怎么建树。线段树其实就是一个二叉树,每个人结点管辖一个区间。所以,我们开始可以建树了。左孩子有孩子分别递归一下,tree_{i}.sum=tree_{i*2}.sum+tree_{i*2+1}.sum

void build(int idx,int l,int r){tree[idx].l=l;tree[idx].r=r;if(l==r){//如果这个节点是叶子节点tree[i].sum=a[l];return;}int mid=(l+r)>>1;build(idx*2,l,mid);//分别构造左子树和右子树build(idx*2+1,mid+1,r);tree[idx].sum=tree[idx*2].sum+tree[idx*2+1].sum;
}

顺便多一嘴,线段树一般开4倍空间(除非你动态开点),但下面也有提及。

单点修改 区间查询

→单点修改

这个不难,直接向下递归就完事儿了。返回时按tree_{i}.sum=tree_{i*2}.sum+tree_{i*2+1}.sum做就可以了。

void modify(int idx,int k,int x){if(tree[idx].l==tree[idx].r){//找到了tree[idx].sum+=x;return;}if(k<=tree[idx*2].r)modify(idx*2,k,x);elsemodify(idx*2+1,k,x);tree[idx].sum=tree[idx*2].sum+tree[idx*2+1].sum;//返回更新return;
}

→区间查询

接下来我们看看如何区间查询。我们其实就找我们来看看哪些区间被目标区间包含了。所以,从根节点开始往下递归,如果当前结点是被要查询的区间包含了,则返回这个结点的信息,时间复杂度为\Theta (logn)

int query(int idx,int l,int r){if(tree[i].l>=l && tree[i].r<=r)//如果区间被包括在目标区间里面,直接返回return tree[i].sum;if(tree[i].r<l || tree[i].l>r)//八竿子打不着return 0;int res=0;if(tree[idx*2].r>=l)res+=query(idx*2,l,r);//左端点和目标区间有交集,搜左子树if(tree[idx*2+1].l<=r)res+=query(idx*2+1,l,r);//搜右子树return res;
}

区间修改 单点查询

区间修改的话,就把这个区间加上一个k的标记 ,单点查询的时候,就从上跑到下,把沿路的标记加起来就好。

→区间修改

void modify(int idx,int l,int r,int k) {if(tree[idx].l>=l && tree[idx].r<=r){tree[idx].tag+=k;return;}int mid=(tree[idx].l+tree[idx].r)>>1;if(l<=mid)modify(idx*2,l,r,k);if(r>mid)modify(idx*2+1,l,r,k);
}

→单点查询

void query(int pos,int x){int res+=tree[pos].tag;if(tree[pos].l==tree[pos].r)return res;int mid=(tree[pos].l+tree[pos].r)>>1;if(x<=mid)query(pos<<1,x);else	query(pos<<1|1,x); 
}

到这,你就可以去做洛谷P3368了。今天元宵节,我就不给你们挖坑了(其实是懒)。

#include <bits/stdc++.h>
using namespace std;
const int maxn=500005;
int ans;
int a[maxn];	
struct node{int l,r;int num;
}tr[maxn*4];//线段树开四倍空间 
void build(int p,int l,int r){tr[p]={l,r,0};if(l==r){tr[p].num=a[l];return ;}int mid=(l+r)>>1;build(p<<1,l,mid);build(p<<1|1,mid+1,r);
}	
void modify(int p,int l,int r,int k){if(tr[p].l>=l && tr[p].r<=r){tr[p].num+=k;return;}int mid=(tr[p].l+tr[p].r)>>1;if(l<=mid)modify(p<<1,l,r,k);if(r>mid)modify(p<<1|1,l,r,k);
}
void query(int p,int x){ans+=tr[p].num;if(tr[p].l==tr[p].r)return;int mid=(tr[p].l+tr[p].r)>>1;if(x<=mid)query(p<<1,x);elsequery(p<<1|1,x); 
}
int main(){int n,m;cin>>n>>m;for(int i=1;i<=n;i++)cin>>a[i];build(1,1,n);for(int i=1;i<=m;i++){int op;cin>>op;if(op==1){int x,y,k;cin>>x>>y>>k;modify(1,x,y,k);}else{ans=0;int x;cin>>x;query(1,x);cout<<ans<<endl;}}return 0
}

还是没忍住,挖了一个坑,你们自己找吧ψ(`∇´)ψ

区间查询 区间修改(懒标记lazy tag)

读完这个,你才能说你会线段树。

当然,这个可以说是最难的一个part了。你可能会说把前面的代码直接拉来用不就结束了吗,但用不了多久你就会发现这个想法非常"智慧"。

那老办法行不通了,有什么新办法吗?当然有!它就是懒标记。懒标记是怎么个回事呢?还是刚刚

那张图。

比如我们要给[1,5]每一个加一,那么向下递归时,可以先不直接操作,而是将管辖那个区间的"领导"的值加上1*5。当然递归回去的时候不要忘记更新上面的结点。(不要问我为什么一定要这么干,因为我也不知道)

先看一下修改的代码。

void modify(int idx,int l,int r,int k){if(tree[idx].r<=r && tree[idx].l>=l){tree[idx].sum+=k*(tree[idx].r-tree[idx].l+1);tree[idx].lazy+=k;//记录lazytagreturn;}push_down(idx);//向下传递if(tree[idx*2].r>=l)modify(idx*2,l,r,k);if(tree[idx*2+1].l<=r)modify(idx*2+1,l,r,k);tree[idx].sum=tree[idx*2].sum+tree[idx*2+1].sum;return;
}

这个是push_down的代码↓

void push_down(int idx){//清空lazytag if(tree[idx].lazy!=0){tree[idx*2].lazy+=tree[idx].lazy;tree[idx*2+1].lazy+=tree[idx].lazy;int mid=(tree[idx].l+tree[idx].r)/2;tree[idx*2].sum+=tree[idx].lazy*(mid-tree[idx*2].l+1);tree[idx*2+1].sum+=tree[idx].lazy*(tree[idx*2+1].r-mid);tree[idx].lazy=0;//归零}return;
}

那查询呢?一样。只要没有被完全包含在目标区间里就push_down,下放懒标记。并让每个区间加上(r-l) \times lazy

int query(int i,int l,int r){if(tree[i].l>=l && tree[i].r<=r)return tree[i].sum;if(tree[i].r<l || tree[i].l>r)return 0;push_down(i);int res=0;if(tree[i*2].r>=l)res+=query(i*2,l,r);if(tree[i*2+1].l<=r)res+=query(i*2+1,l,r);return res;
}

ok,以上就是本期的全部内容。如果有什么问题,请在评论区指正。我们下期再见!

友情提醒:洛谷P3368的代码有问题,请不要无脑Ctrl C+Ctrl V

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

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

相关文章

Spring Boot利用Kaptcha生成验证码

生成验证码 我们在登录或注册某个网站的时候&#xff0c;会需要我们输入验证码&#xff0c;才能登录注册&#xff0c;那么如何生成验证码呢&#xff1f;其实&#xff0c;生成验证码我们可以用Java Swing在后台内存里的区域画一个出来&#xff0c;但是非常麻烦&#xff0c;所以…

在IDEA中创建vue hello-world项目

工作中最近在接触vue前端项目,记录一下从0搭建一个vue hello world项目的步骤 1、本地电脑安装配置node、npm D:\Project\vue\hello-world>node -v v14.21.3 D:\Project\vue\hello-world>npm -v 6.14.18 D:\Project\vue\hello-world> 2、设置npm国内淘宝的景象 …

unity学习(41)——创建(create)角色脚本(panel)——UserHandler(收)+CreateClick(发)——发包!

1.客户端的程序结构被我精简过&#xff0c;现在去MessageManager.cs中增加一个UserHandler函数&#xff0c;根据收到的包做对应的GameInfo赋值。 2.在Model文件夹下新增一个协议文件UserProtocol&#xff0c;内容很简单。 using System;public class UserProtocol {public co…

涵盖5大领域的机器学习工具介绍

随着数据的产生及其使用量的不断增加&#xff0c;对机器学习模型的需求也在成倍增加。由于ML系统包含了算法和丰富的ML库&#xff0c;它有助于分析数据和做出决策。难怪机器学习的知名度越来越高&#xff0c;因为ML应用几乎主导了现代世界的每一个方面。随着企业对这项技术的探…

Java中PDF文件传输有哪些方法?

专栏集锦&#xff0c;大佬们可以收藏以备不时之需&#xff1a; Spring Cloud 专栏&#xff1a;http://t.csdnimg.cn/WDmJ9 Python 专栏&#xff1a;http://t.csdnimg.cn/hMwPR Redis 专栏&#xff1a;http://t.csdnimg.cn/Qq0Xc TensorFlow 专栏&#xff1a;http://t.csdni…

记录一些mac电脑重装mysql和pgsql的坑

为什么要重装,是想在mac电脑 创建data目录…同事误操作,导致电脑重启不了.然后重装系统后,.就连不上数据库了.mysql和pgsql两个都连不上.网上也查了很多资料.实在不行,.就重装了… 重装mysql. 1.官网下载 https://www.mysql.com/downloads/ 滑到最下面 选择 选择对应的芯片版本…

设计推特(Leetcode355)

例题&#xff1a; https://leetcode.cn/problems/design-twitter/ 分析&#xff1a; 推特其实类似于微博&#xff0c;在微博中可以发送文章。 求解这类题目&#xff0c;我们需要根据题目需求&#xff0c;利用面向对象的思想&#xff0c;先对需求做一个抽象&#xff0c;看看能…

字符串(算法竞赛)--Manacher(马拉车)算法

1、B站视频链接&#xff1a;F05 Manacher(马拉车)_哔哩哔哩_bilibili 题目链接&#xff1a;【模板】manacher - 洛谷 ​ #include <bits/stdc.h> using namespace std; const int N3e7; char a[N],s[N]; int d[N];//回文半径函数void get_d(char*s,int n){d[1]1;for(int…

领域驱动设计(Domain-Driven Design DDD)——通过重构找到深层次模型2

五、应用分析模式 深层模型和柔性设计并非唾手可得。想要取得进展&#xff0c;必须学习大量领域知识并进行充分的讨论&#xff0c;还需要经历大量的尝试和失败。在实际的研究领域问题实践时&#xff0c;有一些成熟的模式可以供我们借鉴和套用。这样我们可以从这个起点来重构和试…

vim恢复.swp [BJDCTF2020]Cookie is so stable1

打开题目 扫描目录得到 关于 .swp 文件 .swp 文件一般是 vim 编辑器在编辑文件时产生的&#xff0c;当用 vim 编辑器编辑文件时就会产生&#xff0c;正常退出时 .swp 文件被删除&#xff0c;但是如果直接叉掉&#xff08;非正常退出&#xff09;&#xff0c;那么 .swp 文件就会…

spring-security 过滤器 (三)

spring-security过滤器 版本信息过滤器配置过滤器配置相关类图过滤器加载过程创建 HttpSecurity Bean 对象创建过滤器 过滤器作用ExceptionTranslationFilter 自定义过滤器 本章介绍 spring-security 过滤器配置类 HttpSecurity&#xff0c;过滤器加载过程&#xff0c;自定义过…

信息抽取(UIE):使用自然语言处理技术提升证券投资决策效率

一、引言 在当今快速变化的证券市场中&#xff0c;信息的价值不言而喻。作为一名资深项目经理&#xff0c;我曾领导一个关键项目&#xff0c;旨在通过先进的信息抽取技术&#xff0c;从海量的文本数据中提取关键事件&#xff0c;如企业并购、新产品发布以及政策环境的变动。这些…

Open CASCADE学习|几何数据结构

在几何引擎内一般把数据分成两类&#xff1a;几何信息与拓扑信息。二者可以完整地表达出实体模型&#xff0c;彼此相互独立、又互相关联。几何信息是指构成几何实体的各几何元素在欧式空间中的位置、大小、尺寸和形状信息。例如一条空间的直线&#xff0c;可以用两端点的位置矢…

五种多目标优化算法(MOCS、MOFA、NSWOA、MOAHA、MOPSO)性能对比(提供MATLAB代码)

一、5种多目标优化算法简介 多目标优化算法是用于解决具有多个目标函数的优化问题的一类算法。其求解流程通常包括以下几个步骤&#xff1a; 1. 定义问题&#xff1a;首先需要明确问题的目标函数和约束条件。多目标优化问题通常涉及多个目标函数&#xff0c;这些目标函数可能…

【人脸朝向识别与分类预测】基于BP神经网络

课题名称&#xff1a;基于BP神经网络的人脸朝向识别分类 版本日期&#xff1a;2024-02-20 运行方式&#xff1a;直接运行BP0503.m文件 代码获取方式&#xff1a;私信博主或 QQ:491052175 模型描述&#xff1a; 采集到一组人脸朝向不同角度时的图像&#xff0c;图像来自不同…

17.材质和外观

1.图形学中的材质 在图形学中&#xff0c;材质&#xff08;Material&#xff09;是用来描述物体外观和表面特性的属性集合。它包含了控制光的反射、折射、吸收以及其他光学效果的信息&#xff0c;从而决定了物体在渲染过程中的外观。 渲染方程中那一项和材质有关&#xff1f; …

c#高级-正则表达式

正则表达式是由普通字符和元字符&#xff08;特殊符号&#xff09;组成的文字形式 应用场景 1.用于验证输入的邮箱是否合法。 2.用于验证输入的电话号码是否合法。 3.用于验证输入的身份证号码是否合法。等等 正则表达式常用的限定符总结&#xff1a; 几种常用的正则简写表达式…

[VNCTF2024]-Web:CheckIn解析

查看网页 一款很经典的游戏&#xff0c;而且是用js写的 在调试器里面我们可以看见&#xff0c;如果游戏通关的话&#xff0c;它会进行一系列操作&#xff0c;包括使用console.log(_0x3d9d[0]);输出_0x3d9d[0]到控制台&#xff0c;那我们就直接在点击在控制台求出它的值

C++ //练习 8.9 使用你为8.1.2节(第281页)第一个练习所编写的函数打印一个istringstream对象的内容。

C Primer&#xff08;第5版&#xff09; 练习 8.9 练习 8.9 使用你为8.1.2节&#xff08;第281页&#xff09;第一个练习所编写的函数打印一个istringstream对象的内容。 环境&#xff1a;Linux Ubuntu&#xff08;云服务器&#xff09; 工具&#xff1a;vim 代码块 /*****…

独立版表情包小程序完整版源码前后端源码,附带系统搭建教程

搭建要求&#xff1a; 1.系统要求Nginx 1.18.0PHP-7.2mysql5.6&#xff0c;开启 ssl&#xff0c;php需要安装 sg11 扩展 2.设置伪静态 location / { index index.php index.html index.htm; if (!-e $request_filename) { rewrite ^/(.*)$ /index.php?s$1; } } location /a…