zoukankan      html  css  js  c++  java
  • 线段树

    这篇文章用来总结三种基础线段树模板。

     

    第一种:

    给出N个数,支持以下两种操作

    ·Q  x  y求从第x个元素到y元素的和。

    M   x  v将位置为x的元素修改成v。

     

    下面是模板,其中宏定义lson,rson分别为左儿子,右儿子。比较方便。

     

    #include<cstdio>
    #include<algorithm>
    #define lson l,m,rt<<1
    #define rson m+1,r,rt<<1|1
    #define N 500009
    using namespace std;
    
    int w[N<<2|1];
    
    void update(int rt){                //更新操作,将左子树与右子树合并 
        w[rt] = w[rt << 1]+w[rt<<1|1]; 
    }
    
    int read(){                            //读入优化 
        int x = 0;
        char ch = getchar();
        while(ch < '0' || ch > '9')ch = getchar();
        while(ch >= '0' && ch <= '9'){
            x = x * 10 + ch - '0';
            ch = getchar();
        }
        return x;
    }
    
    void modify(int l,int r,int rt,int p,int v){    //修改操作,将位置为p的元素修改为v 
        if(l == r){                                    //达到要求要修改的节点,则对它进行修改 
            w[rt] = v;
            return;
        }
        int m = (l + r)>>1;
        if(p <= m)modify(lson,p,v);                //在左子树上则递归修改左子树 
        else modify(rson,p,v);                    //在右子树上则修改右子树 
        update(rt);                                //修改完成进行更新 
    }
    
    void build(int l,int r,int rt){                //建树 
        if(l == r){                                //叶子节点 
            w[rt] = read();                        //读入 
            return;
        }
        int m = (l +r)>>1;
        build(lson);                        //如果是非叶子节点则分别递归构造左子树与右子树 
        build(rson);
        update(rt);                            //左子树与右子树都已建造完成,进行更新 
    }
    
    
    int query(int l,int r,int rt,int nowl,int nowr){    //询问操作,询问区间[nowl,nowr] 和 
        if(nowl <= l && r <= nowr)return w[rt];            //当前节点在所求区间内则返回节点值 
        
        int m = (l + r)>>1;
        int ans = 0;    
        if(nowl <= m)ans += query(lson,nowl,nowr);    //左边子树残余范围 
        if(nowr > m)ans += query(rson,nowl,nowr);    //右边 
        return ans;                                    //返回 
    }
    
    
    
    int main(){
        int n = read(),m = read();
        build(1,n,1);
        while(m--){
            int cmd = read();
            if(cmd == 1){                    //1为求区间和 
                int x = read(),y = read();
                printf("%d
    ",query(1,n,1,x,y));
            }
            else {
                int x = read(),v = read();
                modify(1,n,1,x,v);            //2为修改 
            }
        }
        return 0;
    }

    第二种:

    给定长度为N的序列,支持以下两种操作。

    ·Q  x  y    求区间[x,y]序列的和

    ·M  x  y  v    将区间[x,y]的每个数加上一个值v。

     

    对于Q操作,我们处理的方法与第一种几乎是别无二致的。

    但对于M操作,我们在这用到了打标记的方法。

     

    #include<cstdio>
    #include<algorithm>
    #define lson l,m,rt<<1
    #define rson m+1,r,rt<<1|1
    #define N 50009
    using namespace std;
    
    #ifdef WIN32                //条件编译,使得该程序输出不受不同平台下longlong带来的影响 
    #define LL "%I64d
    "
    #else
    #define LL "%lld
    "
    #endif
    
    long long w[N<<2|1],add[N<<2|1];        
    
    int read(){
        int x = 0;
        char ch = getchar();
        while(ch  < '0' || ch > '9')ch = getchar();
        while(ch >= '0' && ch <= '9'){
            x =x * 10 + ch -'0';
            ch = getchar();
        }
        return x;
    }
    
    void update(int rt){
        w[rt] = w[rt<<1]+w[rt<<1|1];
    }
    
    void build(int l,int r,int rt){
        if(l == r){
            w[rt] = read();
            add[rt] = 0;
            return;
        }
        
        int m = (l + r)>>1;
        build(lson);
        build(rson);
        update(rt);
    }
    
    void color(int l,int r,int rt,int v){            //对节点进行标记 
        w[rt] += (r-l+1)*v;
        add[rt] += v;
    }
    
    void push_col(int l,int r,int rt){                //发放标记 
        if(add[rt]){
            int m = (l+r)>>1;
            color(lson,add[rt]);
            color(rson,add[rt]);
            add[rt] = 0;
        }
    }
    
    void modify(int l,int r,int rt,int nowl,int nowr,int v){
        if(nowl <= l && r <= nowr){
            color(l,r,rt,v);
            return;
        }
        push_col(l,r,rt);                    //发放标记 
        int m = (l+r)>>1;
        if(nowl <= m)modify(lson,nowl,nowr,v);
        if(m < nowr)modify(rson,nowl,nowr,v);
        update(rt);
    }
    
    long long query(int l,int r,int rt,int nowl,int nowr){
        if(nowl <= l && r <= nowr)return w[rt];
        push_col(l,r,rt);                        //发放标记 
        int m = (l+r)>>1;
        long long ans = 0;
        if(nowl <= m)ans += query(lson,nowl,nowr);
        if(m < nowr)ans += query(rson,nowl,nowr);
        return ans;
    }
    
    int main(){
        int n = read(),m = read();
        build(1,n,1);
        while(m--){
            int cmd = read();
            if(cmd == 1){
                int x = read(),y = read();
                printf(LL,query(1,n,1,x,y));
            }
            else{
                int x = read(),y = read(),v = read();
                modify(1,n,1,x,y,v);
            }
        }
        return 0;
    }

    第三种:

    给定长度为N的序列,支持以下操作。

    · Q  x  y 求区间[x,y]的和

    ·   M1  x  y  v  将区间[x,y]的每个数都乘以v

    ·  M2  x  y   k 将区间[x,y]的每个数加上k

    ·  M3  x  y   v   k将区间[x,y]的每个数先乘以v再加上k

     

    看代码....

    #include<cstdio>
    #include<algorithm>
    #define lson l,m,rt<<1
    #define rson m+1,r,rt<<1|1
    #define N 50009
    using namespace std;
    
    #ifdef WIN32
    #define LL "%I64d
    "
    #else
    #define LL "%lld
    "
    #endif
    
    
    long long w[N<<2|1],add[N<<2|1],mul[N<<2|1];    //和  加 乘 
    
    
    int read(){
        int x = 0;
        char ch = getchar();
        while(ch < '0' || ch > '9')ch = getchar();
        while(ch >= '0' && ch <= '9'){
            x = x * 10 + ch -'0';
            ch = getchar();
        }
        return x;
    }
    
    void update(int rt){
        w[rt] = w[rt<<1] + w[rt<<1|1];
    }
    
    void build(int l,int r,int rt){
        add[rt] = 0;
        mul[rt] = 1;                    //乘的初始化应为1 
        if(l == r){
            w[rt] = read();
            return;
        }
        int m = (l+r)>>1;
        build(lson);
        build(rson);
        update(rt);
    }
    
    void color(int l,int r,int rt,int v,int k){
        w[rt] = w[rt]*v + (r-l+1)*k;
        add[rt] = add[rt]*v+k;
        mul[rt] *=v;
    }
    
    void push_col(int l,int r,int rt){
        if(add[rt] || mul[rt] != 1){
            int m = (l+r)>>1;
            color(lson,mul[rt],add[rt]);
            color(rson,mul[rt],add[rt]);
            add[rt] = 0;
            mul[rt] = 1;
        }
    }
    
    void modify(int l,int r,int rt,int nowl,int nowr,int v,int k){
        if(nowl <= l && r <= nowr){
            color(l,r,rt,v,k);
            return;
        }
        push_col(l,r,rt);
        int m = (l+r)>>1;
        if(nowl <= m)modify(lson,nowl,nowr,v,k);
        if(m < nowr)modify(rson,nowl,nowr,v,k);
        update(rt);
    }
    
    long long query(int l,int r,int rt,int nowl,int nowr){
        if(nowl <= l && r <= nowr)return w[rt];
        push_col(l,r,rt);
        int m = (l+r)>>1;
        long long ans = 0;
        if(nowl <= m)ans += query(lson,nowl,nowr);
        if(m < nowr)ans += query(rson,nowl,nowr);
        return ans;
    }
    
    int main(){
        int n = read(), m = read();
        build(1,n,1);
        while(m--){
            int cmd = read();
            if(cmd == 1){
                int x = read(),y = read();
                printf(LL,query(1,n,1,x,y));
            }
            else if(cmd == 2){            //
                int x = read(),y = read(),v = read();
                modify(1,n,1,x,y,v,0); 
            }
            else {
                int x = read(),y = read(),k = read();
                modify(1,n,1,x,y,1,k);
            }
        }
        return 0;
    }
  • 相关阅读:
    写给自己的话
    软件开发之技能梳理
    《创新者的窘境》读书笔记
    我的四年踩坑史以及思考
    认识问题和求解问题的一种思考框架
    《时间的秩序》读书笔记
    从JSON中自动生成对应的对象模型
    考考你:一道题引发的小思考
    哈!如果一生只如花样短暂
    使用正则表达式抽取所需文本
  • 原文地址:https://www.cnblogs.com/bingdada/p/7728626.html
Copyright © 2011-2022 走看看