基础线段树

本文详细介绍了线段树在处理单点修改和区间查询问题中的应用,包括区间最大值、最大连续子段和、区间最大公约数等。文章讨论了线段树的结构体设计、维护方法、以及如何利用差分简化区间修改问题。此外,还提到了扫描线优化的线段树在解决矩形覆盖面积和窗口亮度问题中的作用。
摘要由CSDN通过智能技术生成

一、单点修改,区间查询

(一)查询某区间内最大值:acwing最大数

如果是静态问题,可以用RMQ(倍增)来写。
单点修改可以不用懒标记,尽量不用,麻烦。
线段树里的每一个点都是一个结构体,具体存什么根据题目而定:

  1. 问的是什么就存什么,比如【区间查询】问的就是某个区间的某种属性,就要存区间的左右端点位置和这个属性;
  2. 辅助信息,看一下当前属性能不能由两个子区间的属性算出来,如果不能就需要辅助信息

递归建树,比如节点i的两个子区间分别是2i和2i+1,只有叶子节点被真实赋值:

  1. 从i=1开始建树
  2. 如果l=r,也就是叶子节点,就赋值,否则递归
  3. 维护,一般需要,偶尔建的树为空就不需要

维护有两种,用子区间维护父亲区间(从下往上维护),用父亲区间维护子区间(从上往下维护):

  1. 从下往上,pushup。找的时候从上往下找,找到底(叶子节点)并修改,修改完回溯,回溯的时候更新父节点的信息
  2. 从上往下,pushdown。把父节点的修改,更新到儿子节点上。

单点修改:和建树一样用递归,从根节点1开始找要查询的叶子节点的位置,找到就修改,pushup更新父节点的信息。
区间最值查询:一样用递归从1开始找,找到多个在区间内的树枝,对它们的属性求max。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
struct Node {
   
	int l, r;
	int v;
} tr[200010 * 4];
ll n,m,p,last,t;
char op;
void build(int u, int l, int r) {
   
	tr[u].l=l,tr[u].r=r;
	if(l==r) return;
	int mid=l+r>>1;
	build(u<<1, l, mid);
	build(u<<1|1, mid+1, r);
}
int query(int u, int l, int r) {
   	//找所有完全包含的树枝 
	if(l<=tr[u].l&&tr[u].r<=r) return tr[u].v;
	int mid=(tr[u].l+tr[u].r)>>1;
	int v=0;
	if(l<=mid) v=query(u<<1, l, r);
	if(r>mid) v=max(v, query(u<<1|1, l, r));	//找到的各部分再求最大 
	return v;
}
void modify(int u) {
   	//找n+1这个叶子节点 
	if(tr[u].l==tr[u].r&&tr[u].l==n) tr[u].v=(last+t)%p;
	else {
   
		int mid=(tr[u].l+tr[u].r)>>1;
		if(n<=mid) modify(u<<1);
		else modify(u<<1|1);
		tr[u].v = max(tr[u<<1].v, tr[u<<1|1].v);	//修改后从下往上维护 
	}
}
int main() {
   
	scanf("%lld%lld",&m,&p);
	build(1,1,m);
	for(int i=1; i<=m; i++) {
   
		getchar();
		scanf("%c%lld",&op,&t);
		if(op=='A') {
   
			n++;
			modify(1);	//修改n+1 
		} else {
   
			last = query(1, n-t+1, n);	//[n-t+1, n]内最值 
			printf("%lld\n",last);
		}
	}
	return 0;
}

(二)查询某区间内最大连续子段和:acwing你能回答这些问题吗

结构体里如果只存左右端点和当前区间的最大连续子段和:当前区间的属性不能由左右两个子区间的属性得到。所以考虑如何用左右两个子区间的属性得到当前区间的属性:

  1. 左右区间的属性:lmax和rmax
  2. 跨两个区间的属性,左区间的后缀、右区间的前缀:l和r

别忘了考虑新加的属性能不能直接求:

  1. 左右区间的最大连续子段和:可以直接根据左右子区间得到
  2. 包含左区间最后一个数的最大连续子段和、包含右区间第一个数的最大连续子段和。

例如,对于当前区间,它的最大前缀和:它的左子区间的最大前缀和、它的左子区间和和它的右子区间的最大前缀和。它的最大后缀和同理。

所以还有加上一个属性:区间和。
所以要存:

  1. 区间左右端点位置:l和r
  2. 区间内的最大连续子段和:max
  3. 区间的最大连续前缀和、区间的最大连续后缀和:lx和rx
  4. 区间和:s

修改和建树差不多,区别在于建树是修改所有叶子区间,而修改只需修改一个叶子区间
查询,有两种思路。
\quad 一是y总的四种情况,l<ul<ur<r直接回溯、l<ul<r<mid<r搜完左子树回溯、ul<mid<l<ur<r搜完右子树回溯、ul<l<r<ur两个子树都搜;
\quad 二其实也是分四种情况,l<ul<ur<r、ul<l<mid<ur<r、l<ul<mid<r<ur、ul<l<r<ur,只是最后一种情况在写的时候被第二、三种情况包含了,就变成了三种情况,l<ul<ur<r、l<mid<ur<r搜左子树、l<ul<mid<r搜右子树,左右子树都搜完,取了最大,再回溯。
显然,y总的版本更快。

#include<bits/stdc++.h>
using namespace std;
struct T {
   
	int l,r,s,lx,rx,max;
};
T t[2000010];
int n,m,a[500010];
void build(int p, int l, int r) {
   	//建树 
	t[p].l=l,t[p].r=r;
	if(l==r) {
   
		t[p].s=t[p].lx=t[p].rx=t[p].max=a[l];
		return ;
	}
	int mid=(l+r)>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
	//pushup
	t[p].s=t[p<<1].s+t[p<<1|1].s;
	t[p].lx=max(t[p<<1].lx,t[p<<1].s+t[p<<1|1].lx);
	t[p].rx=max(t[p<<1|1].rx,t[p<<1|1].s+t[p<<1].rx);
	t[p].max=max(max(t[p<<1].max,t[p<<1|1].max),t[p<<1].rx+t[p<<1|1].lx);
}
T ask(int p, int l, int r) {
   	//查询 
	if(l<=t[p].l&&t[p].r<=r) return t[p];
	T a,b,ans;
	a.s=a.lx=a.rx=a.max=b.s=b.lx=b.rx=b.max=-0x3f3f3f3f;
	ans.s=0;
	int mid=(t[p].l+t[p].r)>>1;
	if(l<=mid) {
   
		a=ask(p<<1,l,r);
		ans.s+=a.s;
	} 
	if(r>mid) {
   
		b=ask(p<<1|1,l,r);
		ans.s+=b.s;
}
	ans.max=max(max(a.max,b.max),a.rx+b.lx);
	ans.lx=max(a.lx,a.s+b.lx);
	ans.rx=max(b.rx,b.s+a.rx);
	if(l>mid) ans.lx=max(ans.lx,b.lx);
	if(r<=mid) ans.rx=max(ans.rx,a.rx);
	return ans;
}
void change(int p, int x, int w) {
   	//单点修改 
	if(t[p].l==t[p].r) {
   
		t[p].s=t[p].lx=t[p].rx=t[p].max=w;
		return ;
	}
	int mid=(t[p].l+t[p].r)>>1;
	if(x<=mid) change(p<<1,x,w);
	else change(p<<1|1,x,w);
	t[p].s=t[p<<1].s+t[p<<1|1].s;
	t[p].lx=max(t[p<<1].lx,t[p<<1].s+t[p<<1|1].lx);
	t[p].rx=max(t[p<<1|1].rx,t[p<<1|1].s+t[p<<1].rx);
	t[p].max=
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值