线段树是一种二叉树,也可以说成是区间树。线段树主要的就是对区间操作。
线段树很重要的思想就是二分和合并。每一个root管理的都是不同的区间,从最大的区间一直到小区间。递归,回溯是其很重要的思想。
我们为什么要用线段树呢?1、询问快2、更新方便。例如区间求和,我们可以用前缀和去求,但是不方便我们去更新。
操作主要包括单点操作和区间操作。最重要的几个函数:build建树函数,query查询函数,updata更新函数。
下面解释一下区间管理:
root 1 存的从1到n的值,然后就是二分的思想,root 2存的是1到mid的值(mid = (1+n)/2),root 3存的是mid+1到n的值。root 4,root 5,root 6, root 7 继续向下分。每一个向下的root都把区间分的更小。
很好的线段树博客:线段树
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;//单点更新加求区间最大值
const int maxn = 2e5 + 10;
//typedef long long ll;
int dp[maxn << 2];
void push_up(int root){
dp[root] = max(dp[root << 1],dp[root << 1 | 1]);
}
void build(int root,int l,int r){//建树里有既有递归,又有回溯,每个序号里存的都是这个根节点里管理的区间最优解
if(l == r){
scanf("%d",&dp[root]);
return ;
}
int mid = (l + r) >> 1;
build(root<<1,l,mid);
build(root<<1|1,mid+1,r);
push_up(root);
}
void updata(int root,int L,int R,int num,int data){
if(L == R){//如果左临界等于右临界,说明可以进行读入了,这个区间只有一个值
dp[root] = data;
return ;
}
int mid = (L + R) >> 1;
if(num <= mid) updata(root << 1,L,mid,num,data);
else if(num > mid) updata(root << 1 | 1,mid+1,R,num,data);
push_up(root); //更新过之后仍需要回溯,因为上方根管理这个区间
}
int query(int root,int L,int R,int l,int r){
if(L >= l && R <= r) return dp[root];
int ans = 0;
int mid = (L + R) >> 1;
if(l <= mid) ans =max(query(root<<1,L,mid,l,r),ans) ;
if(r > mid) ans = max(query(root<<1|1,mid+1,R,l,r),ans);
return ans;
}
int main(){
int n,m;
char que[3];
while(scanf("%d%d",&n,&m) != EOF){
build(1,1,n);
getchar();
while(m--){
scanf("%s",que);
int num,data;
int lef,rig;
if(que[0] == 'U'){
scanf("%d%d",&num,&data);
updata(1,1,n,num,data);
}else if(que[0] == 'Q'){
scanf("%d%d",&lef,&rig);
int ans = query(1,1,n,lef,rig);
printf("%d\n",ans);
}
}
}
return 0;
}
#include <iostream>
#include <cstdlib>
#include <cmath>
#include <string>
#include <cstring>
#include <cstdio>
#include <set>
#include <vector>
#include <map>
#include <queue>
#include <list>
#include <algorithm>
#define MST(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long ll;
const int MAXN = 50000 + 5000;
struct tree {
int left, right, value;
} T[MAXN << 2];
void build(int x, int l, int r) {
T[x].left = l;
T[x].right = r;
T[x].value = 0;
if (l + 1 == r) return ;
int m = (l + r) >> 1;
build(x << 1, l, m);
build(x << 1 | 1, m, r);
}
void update(int x, int l, int r, int v) {
if (T[x].left > r || T[x].right < l) return ;
if (T[x].left >= l && T[x].right <= r) {
T[x].value = max(T[x].value, v);
return ;
}
update(x << 1, l, r, v);
update(x << 1 | 1, l, r, v);
}
int query(int x, int l, int r) {
if (l + 1 == r) return T[x].value;
T[x << 1].value = max(T[x << 1].value, T[x].value);
T[x << 1 | 1].value = max(T[x << 1 | 1].value, T[x].value);
int m = (l + r) >> 1;
return query(x << 1, l, m) + query(x << 1 | 1, m, r);
}
int n, m;
int main() {
while (scanf("%d %d", &n, &m) != EOF) {
MST(T, 0);
build(1, 0, n);
while (m--) {
int s, t, p;
scanf("%d %d %d", &s, &t, &p);
update(1, s, t, p);
}
for (int i = 1; i <= n * 4; i++)
printf("%d: %d %d %d\n", i, T[i].left, T[i].right, T[i].value);
printf("%d\n", query(1, 0, n));
}
}
#include<cstdio>
using namespace std;
#define maxn 100000+10
typedef long long LL;
struct node{
int l,r,m;//左右中点
LL sum,mark;//权值、tag
}T[maxn<<2];
int a[maxn];
void build(int id,int l,int r){
T[id].l=l;//左端点
T[id].r=r;//右端点
T[id].m=(l+r)>>1;//中点
T[id].mark=0;//初始化标记
if(l==r)//达到端点
{T[id].sum=a[l];return;}//标记和,停止递归并返回
build(id<<1,l,T[id].m);//递归左子树
build(id<<1|1,T[id].m+1,r);//递归右子树
T[id].sum=(T[id<<1].sum+T[id<<1|1].sum);//记录和
}
void update(int id,int l,int r,int val){
if(T[id].l==l&&T[id].r==r)//确定是这一段了
{T[id].mark+=val;return;}//不必递归到叶子结点,打tag
T[id].sum+=(LL)val*(r-l+1);//更新权值
if(T[id].m>=r)//只要更新左子树
update(id<<1,l,r,val);
else if(T[id].m<l)
update((id<<1)+1,l,r,val);//只要更新右子树
else
{
update(id<<1,l,T[id].m,val);//更新左右子树
update(id<<1|1,T[id].m+1,r,val);
}
}
LL query(int id,int l,int r){
if(T[id].l==l&&T[id].r==r)//找到结点
return T[id].sum+T[id].mark*(LL)(r-l+1);//权值+tag
if(T[id].mark)//原来更新到这里的时候没有继续更新下去了(有tag)
{
T[id<<1].mark+=T[id].mark;//tag下传
T[id<<1|1].mark+=T[id].mark;
T[id].sum+=(LL)(T[id].r-T[id].l+1)*T[id].mark;//把tag加回sum
T[id].mark=0;//去掉tag
}
if(T[id].m>=r){
return query(id<<1,l,r);//只有左子树
}
else if(T[id].m<l){
return query(id<<1|1,l,r);//只有左子树
}
else{
return query(id<<1,l,T[id].m)+query((id<<1)+1,T[id].m+1,r);//左右子树都有
}
}
int main(){
int n,Q;
char str[8];
int b,c,d;
scanf("%d%d",&n,&Q);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
build(1,1,n);//建树
for(int i=0;i<Q;i++)
{
scanf("%s",str);
if(str[0]=='Q')
{
scanf("%d%d",&b,&c);
printf("%lld\n",query(1,b,c));//查询
}
else
{
scanf("%d%d%d",&b,&c,&d);
update(1,b,c,d);//更新
}
}
return 0;
}