线段树笔记

109 阅读3分钟

线段树 是用于解决区间问题的一种数据结构,用了分治思想。顾名思义“线段树”,它的每个点都是一段线段,维护着一个数组区间。线段树咨询速度很快,修改也很方便。

既然是分治,那么,一个递归操作就包含结束条件分别递归合并处理三个部分。

一个最基本的的线段树会包含以下几个函数👇

  1. build() ———— 用于建树
  2. modify() ——— 用于修改单点元素
  3. query() ——— 用于咨询区间元素

而且,tree[x]一般表示为x节点的区间值(比如区间最大值,区间和之类的),并不需要存储区间开头或结尾。

除此之外,还有区间修改,一般常用的有懒惰标记标记永久化两个方法。懒惰标记的方法实现需要一个额外lazy数组,用来标记操作。与单点修改不同的是,懒惰标记不需要递归到底层,只需要在包含的修改区间标记一下,如果别的操作遍历过这个节点,再顺便把标记应用到子节点(一般为push_down()函数)。懒惰标记的核心思想是:不使用就不会应用(到子节点)真懒。大大减少了时间复杂度,一般最坏时间复杂度为O(log n) 。可能吧

线段树用于解决区间问题,比如区间最大值,区间和之类的,还有 RMQ 问题。

不说了,上代码:

#include <bits/stdc++.h>
#define int long long
//#define max(a,b) (a>b?a:b)
using namespace std;
//struct linetree
//{
int a[1000000],t[1000000],lazy[1000000];
void build(int l, int r, int x)
{
	if (l==r)
	{
		t[x]=a[l]; 
		return;
	}
	int mid=(l+r)/2;
	build(l,mid,x*2);
	build(mid+1,r,x*2+1);
	t[x]=max(t[x*2+1],t[x*2]);
	return;
}
void push_down(int x)
{
//	t[x]+=lazy[x];
	lazy[x*2]+=lazy[x];
	t[x*2]+=lazy[x];
	lazy[x*2+1]+=lazy[x];
	t[x*2+1]+=lazy[x];
	lazy[x]=0;
}
int query(int l, int r, int x, int L, int R)
{
	if (lazy[x]) push_down(x);
	if (l > R || r < L) return -0x7fffffff;
//	if (l == r) return t[x];
	if (L <= l && r <= R) return t[x];
	int mid=(l+r)/2,ans=-2000000000;
	
	ans=max(ans,query(l,mid,x*2,L,R));
	ans=max(ans,query(mid+1,r,x*2+1,L,R));
	return ans;
}
void modify(int l, int r, int x, int T, int v)
{
	if (l==r) 
	{
		a[T]=v;
		t[x]=v;
		return;
	}
	int mid=(l+r)/2;
	if (lazy[x]) push_down(x);
	if (T <= mid) modify(l,mid,x*2,T,v);
	else modify(mid+1,r,x*2+1,T,v);
	t[x]=max(t[x*2+1],t[x*2]);
	return;
}

void modifymax(int l, int r, int x, int L, int R, int value)
{
	if (l >= L && r <= R)
	{
		lazy[x]+=value;
		t[x]+=value;
		return;
	}
//	if (l > R || r < L) return;
	if (lazy[x]) push_down(x);
	int mid=(l+r)/2;
	if (L <= mid)modifymax(l,mid,x*2,L,R,value);
	if (R > mid) modifymax(mid+1,r,x*2+1,L,R,value);
	t[x]=max(t[x*2],t[x*2+1]);
}
//};
signed main()
{
	int n,m,x,y,z;
	scanf("%lld",&n);
	for (int i = 1 ; i <= n ; ++i) scanf("%lld",&a[i]);
	build(1,n,1);
	scanf("%lld",&m);
	for (int i = 0 ; i < m ; ++i) 
	{
		scanf("%lld",&x);
		if (x == 1)
		{
			scanf("%lld%lld%lld",&x,&y,&z);
			modifymax(1,n,1,x,y,z);
		} else {
			scanf("%lld%lld",&x,&y);
			printf("%lld\n",query(1,n,1,x,y));
		}
	}
	return 0;
}
其他

一些GMOJ上的例题: