概念:
注意: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;
}