线段树

线段树是一种二叉树,也可以说成是区间树。线段树主要的就是对区间操作。
线段树很重要的思想就是二分和合并。每一个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都把区间分的更小。
很好的线段树博客:线段树

单点更新(HDU 1754)

#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;
}

区间更新(FZU1608)

#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));
    }
}

POJ 3468 区间求和更新

#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;  
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值