【线段树】知识点讲解

概念

注意:s数组要开题目原数组的4倍大小。

s[p]这个点它代表着数组下标为l~r的元素的和或者最大最小值,这个l和r与数组结构无关,是人为定义构想的。


方法模板

【对于s数组存储“区间和”时】

一、单点更新(初始化s数组)(单点查询和单点更新一样,只是当l==r达到叶子结点时直接返回s[p]即可

1.单点加减更新

void modify(int p, int l, int r, int x, int v)
{
    s[p] += v;   //相当于在这里up操作
    if (l == r) return; //叶结点则退出
    int mid = (l + r) / 2;
    if (x <= mid) //判断x在左儿子还是右儿子
        modify(p * 2, l, mid, x, v);
    else
        modify(p * 2 + 1, mid + 1, r, x, v);
}

2.单点赋值更新

显然不能再像加减一样写,因为更新的叶节点的每个父节点具体不再是统一的加减操作。用上up函数,把儿子结点的信息更新到父节点。

void up(int p)
{
    s[p] = s[p * 2] + s[p * 2 + 1];
}

void modify(int p, int l, int r, int x, int v)
{
    if (l == r)
    {
        s[p] = v;
        return;
    }
    int mid = (l + r) / 2;
    if (x <= mid)
        modify(p * 2, l, mid, x, v);
    else
        modify(p * 2 + 1, mid + 1, r, x, v);
    up(p);
}

二、区间查询

int query(int p, int l, int r, int x, int y)
{
    if (x <= l && r <= y) return s[p];//若该结点被查询区间包含
    int mid = (l + r) / 2, res = 0;
    if (x <= mid) res += query(p * 2, l, mid, x, y);
    if (y > mid) res += query(p * 2 + 1, mid + 1, r, x, y);
    return res;
}

只需结合以下两张图进行理解记忆:





【s数组存储区间最大or最小值】

一、单点更新(初始化s数组)(单点查询和单点更新一样,只是当l==r达到叶子结点时直接返回s[p]即可

int renew(int p,int l,int r,int x,int v) //单点修改。注意s保存的是最大值 
{
	if(l==r) return s[p]=v;
	int mid=(l+r)/2;
	if(x<=mid) return s[p]=max(s[p*2+1],renew(p*2,l,mid,x,v)); //如果要更新的点在左孩子,那就和右孩子的比较取最大,决定父节点值
	else if(x>mid) return s[p]=max(s[p*2],renew(p*2+1,mid+1,r,x,v));
}

注意:返回值不再是void而是int,因为这里需要有左右孩子的比较。叶节点的返回处注意要赋上值。

二、区间查询

int getmax(int p,int l,int r,int x,int y)
{
	if(x<=l && y>=r) return s[p];
	int mid=(l+r)/2;
	int m=0;
	if(x<=mid) m=max(m,getmax(p*2,l,mid,x,y));
	if(y>mid)  m=max(m,getmax(p*2+1,mid+1,r,x,y));
	return m; 
}


和树状数组的比较:


树状数组没有明确的二叉结构,有类似于前缀和的结构,长这样:





1.如果像之前国赛真题那道求“逆序对”的,确实还是应该离散化后用树状数组做比较好。

2.现在觉得如果是遇到那种存储“最大or最小值”的,树状数组没法做的,它只能存区间和,所以用线段树来做。

3.树状数组没有“单点赋值更新”的吧,都只是加减更新,所以遇到单点赋值更新还是用线段树。


例题(线段树存储区间最大值 模板题):


AC代码:

#include<iostream>
#include<bits/stdc++.h>
using namespace std;
const int maxn=8e5+5;   //注意数组开四倍哦!!!(题中写的:n<=200000)
int s[maxn];

int renew(int p,int l,int r,int x,int v) //单点修改。注意s保存的是最大值 
{
	if(l==r) return s[p]=v;
	int mid=(l+r)/2;
	if(x<=mid) return s[p]=max(s[p*2+1],renew(p*2,l,mid,x,v));
	else if(x>mid) return s[p]=max(s[p*2],renew(p*2+1,mid+1,r,x,v));
}

int getmax(int p,int l,int r,int x,int y)
{
	if(x<=l && y>=r) return s[p];
	int mid=(l+r)/2;
	int m=0;
	if(x<=mid) m=max(m,getmax(p*2,l,mid,x,y));
	if(y>mid)  m=max(m,getmax(p*2+1,mid+1,r,x,y));
	return m;
}

int main()
{
	int n,m;
	char a;
	int b,c;
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&b);
		renew(1,1,n,i,b);
	}
	for(int i=0;i<m;i++)
	{
		scanf(" %c",&a);
		if(a=='Q')
		{
			scanf("%d%d",&b,&c);
			printf("%d\n",getmax(1,1,n,b,c));
		}
		else
		{
			scanf("%d%d",&b,&c);
			renew(1,1,n,b,c);
		}
	}
	return 0;
}




猜你喜欢

转载自blog.csdn.net/m0_38033475/article/details/80376803