线段树
浅谈线段树 - 王陸 - 博客园 (cnblogs.com)
具体例题
区间加法和区间求和: 记录详情 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
区间加法乘法,求和 记录详情 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
SCOI2010] 序列操作 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
基本概念
1、线段树是一棵二叉搜索树,它储存的是一个区间的信息。
2、每个节点以结构体的方式存储,结构体包含以下几个信息:
区间左端点、右端点;(这两者必有)
这个区间要维护的信息(事实际情况而定,数目不等)。
3、线段树的基本思想:二分。
形式如下:
结构体------要开4倍的空间
懒标记:向下传递而没有传下去的信息
懒标记要注意优先级,更新区间时若区间已经有懒标记,要处理优先级/覆盖问题
向下传递时,若下面的区间有懒标记,要处理优先级/覆盖问题
struct node
{
long long sum, add;
int left, right;
};//左右节点,区间和,懒标记,
或者
struct node
{
long long sum, add, mul;
int left, right;
}; //支持乘法操作,mul(乘法的懒标记)
线段树支持的操作有:
1,建树,即建立一棵线段树
//create(1,n,1);//将n个数建立成线段树
void create(int l, int r, int i)
{
a[i].left = l;
a[i].right = r;
a[i].sum = 0;
a[i].add = 0;
if (l == r)
{
cin >> a[i].sum;
return;
}
int mid = (l + r) / 2;
create(l, mid, i << 1);
create(mid + 1, r, (i << 1) + 1);
a[i].sum = a[i << 1].sum + a[(i << 1) + 1].sum;
}
2,单点查询,即查询一个点的状态,设待查询点为x
int query(int x, int i)//x为要查询的第x个数
{
if (a[i].left == a[i].right && x == a[i].left)
{
return a[i].sum;
}
push_down(i);//区间被修改时使用
int mid = (a[i].left + a[i].right) / 2;
if (x <= mid)
query(x, i << 1);
else
query(x, i << 1 | 1);
}
3、单点修改,即更改某一个点的状态,即对第x个数加上y
void update(int i, int x, int y)让第x个数加上y
{
if (a[i].left == a[i].right && a[i].left == x)
{
a[i].sum += y;
return;
}
push_down(i);
int mid = (a[i].left + a[i].right) / 2;
if (x <= mid)
update(i << 1, x, y);
else
update(i << 1 | 1, x, y);
a[i].sum = a[i << 1].sum + a[i << 1 | 1].sum;//向上回溯
}
4、区间查询,即查询一段区间的状态
void query(int l, int r, int i) //查询[l,r]区间的和
//ans为全局变量
{
if (a[i].left == l && a[i].right == r)
{
ans += a[i].sum;
return;
}
push_down(i);
int mid = (a[i].left + a[i].right) / 2;
if (l > mid)
query(l, r, (i << 1) + 1);
else if (r <= mid)
query(l, r, i << 1);
else
{
query(l, mid, i << 1);
query(mid + 1, r, (i << 1) + 1);
}
}
5、区间修改,即修改一段连续区间的值
void update(int l, int r, int i, long long k) //区间[l,r]所有数加上k
{
if (a[i].left == l && a[i].right == r)
{
a[i].sum += k * (r - l + 1);
a[i].add += k;
return;
}
push_down(i);
int mid = (a[i].left + a[i].right) / 2;
if (r <= mid)
update(l, r, i << 1, k);
else if (l > mid)
update(l, r, (i << 1) + 1, k);
else
{
update(l, mid, i << 1, k);
update(mid + 1, r, (i << 1) + 1, k);
}
a[i].sum = a[i << 1].sum + a[(i << 1) + 1].sum;
}
懒标记下传函数
void push_down(int i)
{
if (a[i].add != 0)
{
a[i << 1].add += a[i].add;
a[(i << 1) + 1].add += a[i].add;
a[i << 1].sum += a[i].add * (a[i << 1].right - a[i << 1].left + 1);
a[(i << 1) + 1].sum += a[i].add * (a[(i << 1) + 1].right - a[(i << 1) + 1].left + 1);
a[i].add = 0;
}
}
区间加法和乘法混合时;
区间乘上x时,乘法懒标记mui*=x;加法懒标记add*=x;
区间加上x时,加法懒标记add+=x;
向下传递时:push_down(int i)
先计算乘法,后计算加法,最后add清零,mul变成1
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话