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

线段树相关算法题(1)

hello 大家好,今天是2025年8月24日,我来给大家分享两道线段树相关的算法题。题目难度递增,由简单到中等,学过线段树的朋友们可以来挑战一下。

前言(预备知识)

1. 在解决线段树相关题目时,可以根据下面几个方面来记忆以及修改模板:

a. 根据查询以及修改操作,决定结构体中维护什么信息

b. pushup:根据左右孩子维护的信息,更新当前结点维护的信息

c. pushdown:当前结点的懒信息往下发一层,让左右孩子接收懒信息,并清空当前节点的懒信息

d. lazy:当前区间收到修改操作之后,更新当前结点维护的信息并且把修改信息“懒”下来

e. build:遇到叶子结点返回,否则递归处理左右孩子,然后整合左右孩子的信息

f. modify:遇到完全覆盖的区间,直接修改;否则有懒信息就先分给左右孩子懒信息,然后递归处     理左右区间;最后整合左右孩子的区间

g. query:遇到完全覆盖的区间,直接返回节点维护的信息;否则有懒信息就先分给左右孩子懒信      息,然后整合左右区间的查询信息

2. 实现时候易错的细节问题:

a. lazy 函数只把懒信息存了下来,没有修改区间维护的信息;

b. query 以及 modify 操作,没有分配懒信息;

c. pushdown 之后,没有清空当前结点的懒标记。

3. 线段树如果想要做到单次区间修改操作的时间复杂度为 log(n),那么在一段范围上执行修改        操作之后,需要能够在 O(1)时间内得到需要维护的信息。

题目一:忠诚

题目链接:忠诚

1:题目描述

分析:本题没有弯弯绕,可以当作线段树的模板题。没有修改操作,只有查询操作。

当然了,这道题目是静态问题也可以使用ST表解决,具体可以观看本专栏下的《ST表》。

2:算法原理

线段树:

step1. 结构体 node 中存储什么信息???

根据题目中的查询要求,显然我们需要维护一个区间的区间和

因此 node 当中维护的信息为:区间左端点、区间右端点、区间和

struct node
{int l, r, min;
}tr[N << 2];

step2. pushup 根据左右孩子维护的信息,更新当前结点维护的信息

已知左区间的最小值和右区间的最小值,就可以整合出当前区间的最小值

void pushup(int p)
{tr[p].min = tr[lc].min + tr[rc].min;
}

step3. pushdown & lazy. 由于本题是静态问题,不存在修改操作,自然,懒标记也是不需要的。

step4. build query……基本上都是模板,不再过多赘述了。

分析好上述问题之后,就可以尝试也代码了~~

3:代码实现

#include <iostream>using namespace std;#define lc p << 1
#define rc p << 1 | 1const int N = 1e5 + 10;int n, m;
int a[N];struct node
{int l, r, min;
}tr[N << 2];void build(int p, int l, int r)
{tr[p] = {l, r, a[l]};if(l == r) return;int mid = (l + r) >> 1;build(lc, l, mid);build(rc, mid + 1, r);tr[p].min = min(tr[lc].min, tr[rc].min); 
}int query(int p, int x, int y)
{int l = tr[p].l, r = tr[p].r;if(x <= l && r <= y) return tr[p].min;int mid = (l + r) >> 1, ret = 1e9;if(x <= mid) ret = min(ret, query(lc, x, y));if(y > mid) ret = min(ret, query(rc, x, y));return ret;
}int main()
{cin >> n >> m;for(int i = 1; i <= n; i++) cin >> a[i];build(1, 1, n);while(m--){int x, y; cin >> x >> y;cout << query(1, x, y) << " ";}return 0;
} 

题目二:开关

题目链接:开关

1:题目描述

2:提炼经验(很重要)

经验:对于一个数组中只有两种状态的题目(灯的亮和灭、奇数和偶数、雄性和雌性)我们可以将其中一个状态设为1,将另一个状态设为0。以便简化题目。

就比如本题,我们要查询的是一段区间内亮着的灯的个数,我们可以把亮着的灯设为1,灭了的灯设为0,对于查询操作,就简化成了求区间和的操作,对于修改操作,就简化成了0变1,1变0的操作。

那么本题两种操作就是区间修改和区间查询。可以尝试用线段树来解决。

3:算法原理

线段树:

step1. 结构体 node 中存储什么信息???

根据题目中的查询要求和我们的问题转化,显然我们需要维护一个区间的区间和。(区间中1的个数)

因此 node 当中维护的信息为:区间左端点、区间右端点、区间和

又因为这道题目涉及区间修改操作,因此需要懒标记来优化时间复杂度。

我们来分析,得到一个性质,对于一段区间,如果执行了偶数次修改操作,其实就相当于什么都没有做。如果执行了奇数次操作,相当于区间和由 sum 变为 len - sum。

因此在 node 当中多维护一个 cnt 信息(懒标记)表示对此区间一共执行了多少次操作。

综上所述,node 中维护的信息为:区间左端点、区间右端点、区间和、cnt信息。

struct node
{int l, r, sum, cnt;
}tr[N << 2];

step2. pushup 根据左右孩子维护的信息,更新当前结点维护的信息

已知左区间的区间和和右区间的区间和,就可以整合出当前区间的区间和

void pushup(int p)
{tr[p].sum = tr[lc].sum + tr[rc].sum;
}

step3. pushdown & lazy. 由于本题涉及懒标记下发(区间修改),因此需要 pushdown 和 lazy 操作 

pushdown 操作,我们要做的是:

1.懒标记下发给左孩子   2.懒标记下发给右孩子   3.清空当前结点的懒信息

void pushdown(int p)
{lazy(lc, tr[p].cnt);lazy(rc, tr[p].cnt);tr[p].cnt = 0;
}

lazy 操作,我们要做的是:

新来一个修改信息之后:

1.更新当前结点维护的信息   2.把修改信息“懒”下来

void lazy(int p, int cnt)
{if(cnt % 2 == 1) tr[p].sum = tr[p].r - tr[p].l + 1 - tr[p].sum;tr[p].cnt += cnt;
}

step4. build query……基本上都是模板,不再过多赘述了。

分析好上述问题之后,就可以尝试也代码了~~

4:代码实现

#include <iostream>using namespace std;#define lc p << 1
#define rc p << 1 | 1const int N = 1e5 + 10;int n, m;
struct node
{int l, r, sum, cnt;
}tr[N << 2];void lazy(int p, int cnt)
{if(cnt % 2 == 1) tr[p].sum = tr[p].r - tr[p].l + 1 - tr[p].sum;tr[p].cnt += cnt;
}void pushup(int p)
{tr[p].sum = tr[lc].sum + tr[rc].sum;
}void pushdown(int p)
{lazy(lc, tr[p].cnt);lazy(rc, tr[p].cnt);tr[p].cnt = 0;
}void build(int p, int l, int r)
{tr[p] = {l, r, 0, 0};if(l == r) return;int mid = (l + r) >> 1;build(lc, l, mid); build(rc, mid + 1, r);pushup(p);
}void modify(int p, int x, int y)
{int l = tr[p].l, r = tr[p].r;if(x <= l && r <= y){lazy(p, 1);return;}pushdown(p);int mid = (l + r) >> 1;if(x <= mid) modify(lc, x, y);if(y > mid) modify(rc, x, y);pushup(p);
}int query(int p, int x, int y)
{int l = tr[p].l, r = tr[p].r;if(x <= l && r <= y) return tr[p].sum;pushdown(p);int mid = (l + r) >> 1, sum = 0;if(x <= mid) sum += query(lc, x, y);if(y > mid) sum += query(rc, x, y);return sum;
}int main()
{cin >> n >> m;build(1, 1, n);while(m--){int op, a, b; cin >> op >> a >> b;if(op == 0) modify(1, a, b);else cout << query(1, a, b) << endl;}return 0;
}

好的,今天的分享就到这里了~~再见!!!

http://www.dtcms.com/a/348328.html

相关文章:

  • mybatis过渡到mybatis-plus过程中需要注意的地方
  • 自由学习记录(87)
  • 《飞算Java开发实战:从入门安装到项目部署》
  • FPGA 时序分析(一)
  • ubuntu中的nginx.conf和windows中的nginx.conf内容对比
  • LeetCode 101 刷题 - (1) 第一章 最易懂的贪心算法
  • K8S核心知识点
  • Teams Bot机器人实时语音识别的多引擎的处理
  • 【大语言模型 17】高效Transformer架构革命:Reformer、Linformer、Performer性能突破解析
  • 【机器人零件】蜗轮蜗杆减速器
  • Android面试指南(五)
  • 数据挖掘 4.1~4.7 机器学习性能评估参数
  • RandAR训练自己的数据集
  • 多核多线程消息队列传递指针存在可见性问题吗?
  • 如何安全解密受限制的PDF文件
  • 20年AWS服务进化史
  • 基于Spring Boot与Redis的电商场景面试问答解析
  • 鸿蒙中点击响应时延分析
  • 在 AWS ECS Fargate 上将 Datadog 作为 SideCar 运行
  • NLP:Transformer残差连接的作用(特别分享2)
  • elasticsearch 7.17.23 使用spring data es实现高亮分页,scroll查询分页查询
  • AI+SaaS工具实战:如何靠AI赋能CRM/财务SaaS实现企业降本28%+效率提升40%
  • Vue 项目 package.json 终极详解(主流实践 / 逐项说明)
  • element-plus穿梭框transfer的调整
  • Git连接Github远程仓库的代理设置
  • centerpoint训练过程
  • 包含单负介质的夹层结构中的电磁隧穿现象的复现
  • 设计模式:建造者模式
  • linux下camera 详细驱动流程 OV02K10为例(chatgpt版本)
  • css3之flex布局