zoukankan      html  css  js  c++  java
  • 线段树模板(无lazy优化)

    区间修改与区间查询问题

     模板:

    int ans;
    
    struct node{
        int l,r,v;
        node(){v=0;}
    }tree[LEN*4];
    int arr[LEN];
    //建树 
    void build(int l,int r,int i){    //记录 [ l , r ]上的值,当前索引为 i 
        tree[i].l=l;
        tree[i].r=r;
        if(l==r){    //如果访问到了叶子节点 
            tree[i].v=arr[l];    //当前值就是arr[l,r]的值 
            return;        
        }
        int m=(l+r)/2;    //二分 
        build(l,m,LS(i));    //左子树 
        build(m+1,r,RS(i));    //右子树 
        tree[i].v=tree[LS(i)].v+tree[RS(i)].v;    //递归建树出栈后,前驱结点的值为后继结点的和 
    }
    //区间查询 
    void query(int i,int l,int r){    // i 为当前树的索引,初试化调用为 1 。查询 [ l , r ]区间上的和 
        if(tree[i].l>=l && tree[i].r<=r){ //如果查询区间包裹住了当前结点区间 
            ans+=tree[i].v;        //直接加上这个结点的值 
            return;
        } 
        if(l<=tree[LS(i)].r) //查询的 l 比左子树的右端点小 
            query(LS(i),l,r);    //向左递归 
        if(r>=tree[RS(i)].l) //查询的 r 比右子树的左端点大 
            query(RS(i),l,r);    //向右递归 
    }
    //单点修改 
    void plus_i(int i,int p,int d){    //index(current) position delta
        tree[i].v+=d;        //当前结点的值更新 
        if(tree[i].l==tree[i].r) //访问到了叶子节点 
            return;        //退出 
        if(p<=tree[LS(i)].r)    //查询的 p 比左子树的右端点小 
            plus_i(LS(i), p,d);
        if(p>=tree[RS(i)].l)     //查询的 p 比右子树的左端点大 
            plus_i(RS(i), p,d);
    }
    //区间修改 
    void plus_s(int i,int l,int r,int d){
        //取查询区间与结点区间的交集 
        int tl=max(l,tree[i].l);
        int tr=min(r,tree[i].r);
        if(tl<=tr) tree[i].v+=d*(tr-tl+1);    //如果这个交集合法,区间加 
        else return;
        if(l<=tree[LS(i)].r) //查询的 l 比左子树的右端点小 
            plus_s(LS(i), l,r,d);
        if(r>=tree[RS(i)].l) //查询的 r 比右子树的左端点大 
            plus_s(RS(i), l,r,d);
    }

    注:可以把上文的结构体拆写为3个数组

    测试OJ:P3372 【模板】线段树 1

    测试代码:(因为无lazy优化,只有70分。我理解不了lazy优化)

    #include <stdio.h>
    #include <memory.h>
    #include <math.h>
    #include <string>
    #include <vector>
    #include <set>
    #include <stack>
    #include <queue>
    #include <algorithm>
    #include <map>
    
    #define I scanf
    #define OL puts
    #define O printf
    #define F(a,b,c) for(a=b;a<c;a++)
    #define FF(a,b) for(a=0;a<b;a++)
    #define FG(a,b) for(a=b-1;a>=0;a--)
    #define LEN 100010
    #define MAX 1<<30
    #define V vector<int>
    #define ll long long  
    #define LS(i) i<<1
    #define RS(i) i<<1|1
    
    using namespace std;
    
    int ans;
    
    struct node{
        int l,r,v;
        node(){v=0;}
    }tree[LEN*4];
    int arr[LEN];
    //建树 
    void build(int l,int r,int i){    //记录 [ l , r ]上的值,当前索引为 i 
        tree[i].l=l;
        tree[i].r=r;
        if(l==r){    //如果访问到了叶子节点 
            tree[i].v=arr[l];    //当前值就是arr[l,r]的值 
            return;        
        }
        int m=(l+r)/2;    //二分 
        build(l,m,LS(i));    //左子树 
        build(m+1,r,RS(i));    //右子树 
        tree[i].v=tree[LS(i)].v+tree[RS(i)].v;    //递归建树出栈后,前驱结点的值为后继结点的和 
    }
    //区间查询 
    void query(int i,int l,int r){    // i 为当前树的索引,初试化调用为 1 。查询 [ l , r ]区间上的和 
        if(tree[i].l>=l && tree[i].r<=r){ //如果查询区间包裹住了当前结点区间 
            ans+=tree[i].v;        //直接加上这个结点的值 
            return;
        } 
        if(l<=tree[LS(i)].r) //查询的 l 比左子树的右端点小 
            query(LS(i),l,r);    //向左递归 
        if(r>=tree[RS(i)].l) //查询的 r 比右子树的左端点大 
            query(RS(i),l,r);    //向右递归 
    }
    //单点修改 
    void plus_i(int i,int p,int d){    //index(current) position delta
        tree[i].v+=d;        //当前结点的值更新 
        if(tree[i].l==tree[i].r) //访问到了叶子节点 
            return;        //退出 
        if(p<=tree[LS(i)].r)    //查询的 p 比左子树的右端点小 
            plus_i(LS(i), p,d);
        if(p>=tree[RS(i)].l)     //查询的 p 比右子树的左端点大 
            plus_i(RS(i), p,d);
    }
    //区间修改 
    void plus_s(int i,int l,int r,int d){
        //取查询区间与结点区间的交集 
        int tl=max(l,tree[i].l);
        int tr=min(r,tree[i].r);
        if(tl<=tr) tree[i].v+=d*(tr-tl+1);    //如果这个交集合法,区间加 
        else return;
        if(l<=tree[LS(i)].r) //查询的 l 比左子树的右端点小 
            plus_s(LS(i), l,r,d);
        if(r>=tree[RS(i)].l) //查询的 r 比右子树的左端点大 
            plus_s(RS(i), l,r,d);
    }
    
    int main(){
    //    freopen("D:\CbWorkspace\ACM数据结构\线段树\模板1.txt","r",stdin);
        int N,M,i,t,op,x,y,k;
        I("%d%d",&N,&M);
        for(i=1;i<=N;i++){
            I("%d",&arr[i]);
        }
        build(1,N,1);
        while(M--){
            I("%d%d%d",&op,&x,&y);
            switch(op){
                case 1:
                    I("%d",&k);
                    plus_s(1,x,y,k);
                    break;
                case 2:
                    ans=0;
                    query(1,x,y);
                    O("%d
    ",ans);
                    break;
            }
        }
        return 0;
    }
    View Code

    区间最大最小值查询问题

    OJ 链接:P1198 [JSOI2008]最大数

    AC代码:

    #include <stdio.h>
    #include <memory.h>
    #include <math.h>
    #include <string>
    #include <vector>
    #include <set>
    #include <stack>
    #include <queue>
    #include <algorithm>
    #include <map>
    
    #define I scanf
    #define OL puts
    #define O printf
    #define F(a,b,c) for(a=b;a<c;a++)
    #define FF(a,b) for(a=0;a<b;a++)
    #define FG(a,b) for(a=b-1;a>=0;a--)
    #define LEN 400010
    #define MAX 1<<30
    #define V vector<int>
    #define ll long long  
    #define LS(i) i<<1
    #define RS(i) i<<1|1
    
    using namespace std;
    
    int ans;
    
    struct node{
        int l,r,v;
        node(){v=0;}
    }tree[LEN*4];
    
    //建树 
    void build(int l,int r,int i){    //记录 [ l , r ]上的值,当前索引为 i 
        tree[i].l=l;
        tree[i].r=r;
        tree[i].v=-MAX;
        if(l==r){    //如果访问到了叶子节点 
            return;        
        }
        int m=(l+r)/2;    //二分 
        build(l,m,LS(i));    //左子树 
        build(m+1,r,RS(i));    //右子树 
    }
    //区间查询 
    void query(int i,int l,int r){    // i 为当前树的索引,初试化调用为 1 。查询 [ l , r ]区间上的和 
        if(tree[i].l>=l && tree[i].r<=r){ //如果查询区间包裹住了当前结点区间 
            ans=max(ans,tree[i].v);        //取最大值 
            return;
        } 
        if(l<=tree[LS(i)].r) //查询的 l 比左子树的右端点小 
            query(LS(i),l,r);    //向左递归 
        if(r>=tree[RS(i)].l) //查询的 r 比右子树的左端点大 
            query(RS(i),l,r);    //向右递归 
    }
    //单点修改 
    void plus_i(int i,int p,int d){    //index(current) position delta
        tree[i].v=max(tree[i].v,d);        //取最大值 
        if(tree[i].l==tree[i].r) //访问到了叶子节点 
            return;        //退出 
        if(p<=tree[LS(i)].r)    //查询的 p 比左子树的右端点小 
            plus_i(LS(i), p,d);
        if(p>=tree[RS(i)].l)     //查询的 p 比右子树的左端点大 
            plus_i(RS(i), p,d);
    }
    
    int main(){
    //    freopen("D:\CbWorkspace\ACM数据结构\线段树\最大数.txt","r",stdin);
        int N,D,t=0,num,pos=1;
        build(1,LEN,1);
        char buf[2];
        I("%d%d",&N,&D);
        while(N--){
            I("%s%d",buf,&num);
            if(buf[0]=='A'){
                num+=t;
                num%=D;
                plus_i(1,pos++,num);
            }else{
                if(num==0){
                    O("%d
    ",t=0);
                    continue;
                }
                ans=-MAX;
                query(1,pos-num,pos-1);
                O("%d
    ",t=ans);
            }
        }
        return 0;
    }
  • 相关阅读:
    Linux Shell系列教程之(十七) Shell文件包含
    Linux Shell系列教程之(十六) Shell输入输出重定向
    Linux Shell系列教程之(十五) Shell函数简介
    Linux Shell系列教程之(十四) Shell Select教程
    Linux Shell系列教程之(十三)Shell分支语句case … esac教程
    Linux Shell系列教程之(十二)Shell until循环
    Linux Shell系列教程
    算法系列:链表
    C++ 系列:Boost Thread 编程指南
    C++:多线程002
  • 原文地址:https://www.cnblogs.com/TQCAI/p/8662330.html
Copyright © 2011-2022 走看看