线段树将区间分为若干个子区间,子区间又继续分,直到区间为一个点(l==r)
对于父区间 [l,r] ,其子区间为 [l,(l+r)/2] 和 [(l+r)/2+1,r]
线段树一般用于求区间的值,如区间最值,区间求和等。
代码实现中, 假设节点下标从1开始,若某节点下标为x,那么其左子区间下标为2x,右子区间下标为2x+1,父节点下标为x/2;
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191016164146511.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNzMxMDE5,size_16,color_FFFFFF,t_70)
常用宏定义
#define Mid ((l+r)>>1) //中间值 注意括号
#define lson rt<<1,l,Mid //左结点
#define rson rt<<1|1,Mid+1,r //右结点
建树
建树从根节点开始,递归建立左右子树,直到叶子节点,然后反向赋值,父节点的值=F(左节点的值,右节点的值),这个F根据题意确定,若求区间最大值则为max,若求区间和则为sum。
void build(int rt,int l,int r) //建编号为rt的区间为[l,r]的树,主函数传进来的固定是(1,1,n)
{
if(l==r){ //叶子结点赋初值,注意下标,Max的是编号,val原数组的是l,看图可以理解
Max[rt] = val[l];
}else{ //建左右子树
build(lson);
build(rson);
Max[rt] = max( Max[rt<<1], Max[rt<<1|1]); //父结点Max值为Max(左子结点,右子结点)
}
}
查询
查询为区间查询(只是查询某个点的话不需要线段树),即在区间里查询某个特性值,每次查询都是从跟结点开始往下,根据查询区间和当前区间的区间位置判断是要去左右子区间查询,还是直接返回。如果被查询区间是查询区间的子区间则直接返回子区间的值,如在[1,6]里查询[1,12]就返回[1,6]的值,不再往下查询。
void query(int rt,int l,int r,int L,int R) //在[l,r]里查询[L,R]的值,[L,R]一直不变,[l,r]变
{
if(L <= l && r <= R){
ans1 = max(ans1,Max[rt]);
ans2 = min(ans2,Min[rt]);
}else{
if( L <= Mid) //查询区间在当前区间的左半区间有内容,如在[1,6]里查询[2,3]
query(lson,L,R);
if( R > Mid) //同理去右子区间,注意不能有else,因为有横跨左右的情况,如[1,6]里查询[2,5]
query(rson,L,R);
}
}
单点更新
void update(int rt,int l,int r,int pos,int num)
{
if(l == r && r == pos){ //到对应的叶结点
Max[rt] = num;
}else{
if( pos <= Mid)
update(lson,pos,num);
if( pos > Mid) //或者直接else,点不可能同时在两个区间里
update(rson,pos,num);
Max[rt] = max( Max[rt<<1], Max[rt<<1|1]);
}
}
区间更新
void pushDown(int rt,int len)
{
add[rt<<1] = add[rt<<1|1] = add[rt];
sum[rt<<1] = (len-(len>>1))*add[rt];
sum[rt<<1|1] = (len>>1)*add[rt];
add[rt] = 0;
}
void update(int rt,int l,int r,int L,int R,int z)
{
if(L <= l && r <= R){
add[rt] = z;//或 add[rt]+=z; 根据题意而定
sum[rt] = (r-l+1)*add[rt];
}else{
if(add[rt])
pushDown(rt,r-l+1);
if(L <= Mid)
update(lson,L,R,z);
if(R > Mid)
update(rson,L,R,z);
sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}
}
例题
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <cmath>
#include <cstdio>
#include <map>
using namespace std;
#define mem(a, b) memset(a, b, sizeof(a))
#define PI acos(-1)
typedef long long ll;
int dir8[8][2] = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}, {1, 1}, {1, -1}, {-1, 1}, {-1, -1}};
int dir4[4][2] = {1, 0, 0, 1, -1, 0, 0, -1};
const int INF = 0x3f3f3f3fll;
const long long intF = 0x3f3f3f3f3f3f3f3fll;
const int MAXn = 1e5 + 5;
const int mod = 1e9 + 7;
struct node
{
int l, r, w, flag;
int mid() { return (r + l) / 2; }
int dis() { return r - l + 1; }
} a[MAXn * 4];
void build(int k, int l, int r)
{
a[k].l=l;
a[k].r=r;
a[k].w=0;
a[k].flag=0;
if (l == r)
{
cin >> a[k].w;
return;
}
build(k << 1, l ,a[k].mid());
build(k<<1|1, a[k].mid()+1, r );
a[k].w=a[k<<1].w+a[k<<1|1].w;
}
void down(int k)
{
a[k << 1].w += a[k].flag * a[k << 1].dis();
a[k << 1 | 1].w += a[k].flag * a[k << 1 | 1].dis();
a[k << 1].flag += a[k].flag;
a[k << 1 | 1].flag += a[k].flag;
a[k].flag = 0;
}
void update(int k, int l, int r, int w)
{
if(a[k].l==l&&a[k].r==a[k].l)
{
a[k].w+=a[k].dis()*w;
a[k].flag+=w;
return ;
}
if(a[k].flag)
down(k);
if(a[k].mid()>=l)
update(k<<1,l,r,w);
if(a[k].mid()<r)
update(k<<1|1,l,r,w);
a[k].w=a[k<<1].w+a[k<<1|1].w;
}
int query(int k, int l, int r)
{
if (a[k].l >= l && a[k].r <= r)
return a[k].w;
if (a[k].flag)
down(k);
int sum = 0;
if (a[k].mid() >= l)
sum += query(k << 1, l, r);
if (a[k].mid() < r)
sum += query(k << 1 | 1, l, r);
a[k].w = a[k << 1].w + a[k << 1 | 1].w;
return sum;
}
int main()
{
//IN;
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T,ii=0;
cin >> T;
while (T--)
{
ii++;
cout<<"Case "<<ii<<":"<<endl;
int n;
cin>>n;
build(1,1,n);
while(1)
{
string s;
int i,j;
cin>>s;
if(s=="End")
break;
cin>>i>>j;
if(s=="Sub")
j=-j;
if(s=="Query"){
cout<<query(1,i,j)<<endl;
continue;
}
update(1,i,i,j);
}
}
return 0;
}