zoukankan      html  css  js  c++  java
  • [OI学习笔记]线段树模板

    背景

      今天课上讲了树状数组和线段树,有是一脸懵逼。。。

      于是网上有恶补了一下,

      看了几个小时blog终于懂了。。。

      由于很难解释,就只贴两个模板代码(一个单点修改+单点和区间查询,一个区间修改+单点和区间查询)

    几点注意事项

      1.首先跑区间更新的程序,一般对于每一个点要维护一个lazy变量来临时存储它将要向下更新的数值,然后对于要查寻的点,直接向下更新,这样偷懒更省时间。(具体实现过程见代码)。

      2.空间要开到4*N(N为元素个数)(好像可以优化成2*N,但是本蒟蒻不知道怎么优化)

      3.打线段树坑点还是很多的,下面列举一下我所犯过的错误:

        1st.睿智地把mid=(tree[k].r+tree[k].l)/2和(tree[k*2].r-tree[k*2].l+1)搞混(全国可能就我会这样了。。。)

        2nd.下放lazy更新子节点的权时,要用父节点的lazy更新,并且区间长度要+1.

        3rd.下放完lazy父节点的lazy值要标位0!!防止重复下放!

      大概就是这些,,,下面直接上代码

    代码

      线段树单点修改两种查询:

      

    #include<cstdio>
    #include<iostream>
    using namespace std;
    
    const int MAXN=10010; 
    
    int n;//n个元素,m个操作 
    
    struct node{
        int l,r,v;
    }tree[4*MAXN+1];//其实最少开到4*MAXN就行 
    
    void build(int l,int r,int k){//当前节点存储的线段的左右端点&这个节点的编号 
        tree[k].l=l;tree[k].r=r;
        if(l==r){
            scanf("%d",&tree[k].v);//叶子节点的值可在这里读入! 
            return;
        }
        int mid=(l+r)/2;
        build(l,mid,k*2);
        build(mid+1,r,k*2+1);
        tree[k].v=tree[k*2].v+tree[k*2+1].v;
    }
    
    //单点查询(叶子节点) 
    int askpoint(int x,int k){//目标点在原序列中的下标    k为当前树中的位置下标 
        if(tree[k].l==tree[k].r){
            return tree[k].v;
        }
        int mid=(tree[k].r+tree[k].l)/2;//错点1 //
        if(x<=mid)return askpoint(x,k*2);
        else return askpoint(x,k*2+1);
    }
    
    //单点修改 
    void changepoint(int x,int num,int k){//要在序列中的第x个数中加上num   k为当前树中的下标 
        if(tree[k].l==tree[k].r){
            tree[k].v+=num;
            return;
        }
        int mid=(tree[k].r+tree[k].l)/2;//错点1 //
        if(x<=mid)changepoint(x,num,k*2);
        else changepoint(x,num,k*2+1);
        tree[k].v=tree[k*2].v+tree[k*2+1].v;//重新整合区间和 
    }
    
    //区间查询 
    int sum=0;
    void asksec(int tl,int tr,int l,int r,int k){
        if(l>=tl&&r<=tr){
            sum+=tree[k].v;
            return;
        }
        int mid=(tree[k].l=tree[k].r)/2;
        if(tl<=mid)asksec(tl,tr,l,mid,k*2);
        if(tr>mid)asksec(tl,tr,mid+1,r,k*2+1);//错点2 不是else// 
    }
    
    int main(){
        cout<<"点数"<<endl;//这个程序里不用问题数 
        scanf("%d",&n);
        cout<<"build tree"<<endl; 
        build(1,n,1);
        cout<<"change point"<<endl; 
        int x,num;
        scanf("%d%d",&x,&num);
        changepoint(x,num,1);
        cout<<"ask point"<<endl;
        scanf("%d",&x);
        printf("%d
    ",askpoint(x,1));
        cout<<"ask section"<<endl;
        int tl,tr;
        scanf("%d%d",&tl,&tr);
        asksec(tl,tr,1,n,1);
        printf("%d
    ",sum);
        return 0;
    } 

      线段树区间修改两种查询:

      

    #include<cstdio>
    #include<iostream>
    using namespace std;
    
    const int MAXN=10010; 
    
    int n;//n个元素,m个操作 
    
    struct node{
        int l,r,v,lazy;
    }tree[4*MAXN+1];//其实最少开到4*MAXN就行 
    
    void build(int l,int r,int k){//当前节点存储的线段的左右端点&这个节点的编号 
        tree[k].l=l;tree[k].r=r;
        if(l==r){
            scanf("%d",&tree[k].v);//叶子节点的值可在这里读入! 
            return;
        }
        int mid=(l+r)/2;
        build(l,mid,k*2);
        build(mid+1,r,k*2+1);
        tree[k].v=tree[k*2].v+tree[k*2+1].v;
    }
    
    //下放lazy标记函数
    void putdown(int k){
        tree[k*2].lazy+=tree[k].lazy;
        tree[k*2+1].lazy+=tree[k].lazy;
        tree[k*2].v+=tree[k].lazy*(tree[k*2].r-tree[k*2].l+1);//坑点:1.要用父节点k的lazy  2.区间长度记得要+1//
        tree[k*2+1].v+=tree[k].lazy*(tree[k*2+1].r-tree[k*2+1].l+1);//坑点:同上//
        tree[k].lazy=0;//坑点:父节点的lazy要清0!!!!!// 
    } 
    
    //单点查询(叶子节点) 
    int askpoint(int x,int k){
        if(tree[k].l==tree[k].r){
            return tree[k].v;
        }
        if(tree[k].lazy)putdown(k);//防止重复下放 
        int mid=(tree[k].l+tree[k].r)/2;
        if(x<=mid)return askpoint(x,k*2);
        else return askpoint(x,k*2+1);
    }
    
    //区间修改 
    void changesection(int tl,int tr,int num,int k){
        if(tl<=tree[k].l&&tr>=tree[k].r){//对查询有用 
            tree[k].v+=num*(tree[k].r-tree[k].l+1);//坑点// 
            tree[k].lazy+=num; 
            return;
        }
        if(tree[k].lazy)putdown(k);//防止重复下放 
        int mid=(tree[k].r+tree[k].l)/2;//错点 //
        if(tl<=mid)changesection(tl,tr,num,k*2);
        if(tr>mid)changesection(tl,tr,num,k*2+1);
        tree[k].v=tree[k*2].v+tree[k*2+1].v;
    }
    
    //区间查询 
    int sum=0;
    void asksec(int tl,int tr,int l,int r,int k){
        if(l>=tl&&r<=tr){
            sum+=tree[k].v;
            return;
        }
        if(tree[k].lazy)putdown(k);//防止重复下放 
        int mid=(tree[k].r+tree[k].l)/2;
        if(tl<=mid)asksec(tl,tr,l,mid,k*2);
        if(tr>mid)asksec(tl,tr,mid+1,r,k*2+1);
    }
    
    int main(){
        freopen("in.in","r",stdin);
        cout<<"点数"<<endl;
        scanf("%d",&n);
        cout<<"build tree"<<endl; 
        build(1,n,1);
        cout<<"change section"<<endl; 
        int x,y,num;
        scanf("%d%d%d",&x,&y,&num);
        changesection(x,y,num,1);
        cout<<"ask point"<<endl;
        scanf("%d",&x);
        printf("%d
    ",askpoint(x,1));
        cout<<"ask section"<<endl;
        int tl,tr;
        scanf("%d%d",&tl,&tr);
        asksec(tl,tr,1,n,1);
        printf("%d
    ",sum);
        return 0;
    } 
    本篇文章为SHINE_GEEK原创,转载请注明来源!
    written_by:SHINE_GEEK

    -------------------------------------
    签名:自己选的路,跪着也要走完;理想的实现,需要不懈奋斗!
    -------------------------------------
  • 相关阅读:
    用dockerFile和docker build命令 创建带两个数据卷的新镜像
    Docker 学习 3 镜像更多命令 docker commit 提交容器副本,使之成为一个新的镜像
    Docker学习笔记 2 更多的容器命令
    Docker的学习1 安装 与 基础篇
    SeekBar(拖动条)
    本周总结
    ProgressBar(进度条)
    Android对话框
    css语法
    css基础
  • 原文地址:https://www.cnblogs.com/sjrb/p/10340005.html
Copyright © 2011-2022 走看看