随笔 - 73  文章 - 0 评论 - 0 阅读 - 6680
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

线段树

浅谈线段树 - 王陸 - 博客园 (cnblogs.com)

具体例题

区间加法和区间求和: 记录详情 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

区间加法乘法,求和 记录详情 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
SCOI2010] 序列操作 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

基本概念

1、线段树是一棵二叉搜索树,它储存的是一个区间的信息。

2、每个节点以结构体的方式存储,结构体包含以下几个信息:

区间左端点、右端点;(这两者必有)

这个区间要维护的信息(事实际情况而定,数目不等)。

3、线段树的基本思想:二分

形式如下:

s

结构体------要开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
posted on   naiji  阅读(31)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示