美文网首页数据结构
线段树模板(codvs1080)!

线段树模板(codvs1080)!

作者: 不给赞就别想跑哼 | 来源:发表于2018-06-26 16:32 被阅读7次

    时隔多年又再次遨游在C++的海洋之中。
    话不多说先上题⬇

    *1080 线段树练习

    时间限制: 1 s
    空间限制: 128000 KB
    题目描述
    一行N个方格,开始每个格子里都有一个整数。现在动态地提出一些问题和修改:提问的形式是求某一个特定的子区间[a,b]中所有元素的和;修改的规则是指定某一个格子x,加上或者减去一个特定的值A。现在要求你能对每个提问作出正确的回答。1≤N<100000,,提问和修改的总数m<10000条。

    输入文件第一行为一个整数N,接下来是n行n个整数,表示格子中原来的整数。接下一个正整数m,再接下来有m行,表示m个询问,第一个整数表示询问代号,询问代号1表示增加,后面的两个数x和A表示给位置X上的数值增加A,询问代号2表示区间求和,后面两个整数表示a和b,表示要求[a,b]之间的区间和。

    输出描述

    共m行,每个整数

    样例输入
    6

    4

    5

    6

    2

    1

    3

    4

    1 3 5

    2 1 4

    1 1 9

    2 2 6

    样例输出

    22

    22

    数据范围及提示 <small style="box-sizing: border-box; font-size: 13px;">Data Size & Hint</small>

    1≤N≤100000, m≤10000 。

    不出意外的话,大多数人看到这个题目都会想到暴力执法,可仔细看看数据,暴力是O(nm) 的复杂度,100000*10000肯定是超时的,所以我们只好转换思路。

    那么就开始今天的主题“线段树”。

    顾名思义,它一定是个树的结构,而且是一颗二叉树。
    大概是这样的⤵


    image.png

    每个节点储存的是r,l分别表示该节点存储区间的左右端点,以及ls,rs
    分别表示该节点所连接的左右儿子的节点序号,还有s表示此区间的和。
    从图中可以看出,节点所存储的区间是逐渐二分下去的。如1号节点是1--8,则他的左右儿子2,3分别储存的是1--4,5--8.依此类推。

    执行操作1时从根节点开始判断被修改的点是否在该节点所储存的区间内,如果在,那么就把改节点的s,也就是区间和加上所增加的数值,然后继续判断左右儿子,一直递归下去,便达到了修改数值的目的。

    操作2时,也是从根节点开始,如果所给区间包含了节点储存的区间,那么直接返回该节点的s即可,如果二者完全分离直接返回0,如果以上都不满足,则返回左右儿子的返回值之和。有些抽象,需自己动手领悟。。

    那么就上代码了

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    struct node{int l,r,ls,rs,s;}tree[400010];
    int a[100010],cnt;
    int built(int ll,int rr){
        int u=cnt++;
        tree[u]=(node){ll,rr,-1,-1,0};  
        if(ll<rr){
            int mid=(ll+rr)/2;
            tree[u].ls=built(ll,mid);
            tree[u].rs=built(mid+1,rr);
            tree[u].s=tree[tree[u].ls].s+tree[tree[u].rs].s;
        }
        else tree[u].s=a[ll];
        return u;
    }
    void add(int v,int o,int p){
        if(tree[v].l<=o&&tree[v].r>=o) tree[v].s+=p;
        else return;
        //if(tree[v].l>p||tree[v].r<p) return;
        //if(tree[v].l==tree[v].r)return;
        add(tree[v].ls,o,p);
        add(tree[v].rs,o,p);
    }
    int query(int v,int l1,int r1){
        if(tree[v].l>=l1&&tree[v].r<=r1) return tree[v].s;
        if(tree[v].r<l1||tree[v].l>r1) return 0;
        else return query(tree[v].ls,l1,r1)+query(tree[v].rs,l1,r1);
    }
    int main(){
        int n,m,t,x,y;
        cin>>n;
        for(int i=1;i<=n;i++) cin>>a[i];
        int root=built(1,n);
        cin>>m;
        for(int i=1;i<=m;i++){
            cin>>t>>x>>y;
            if(t==1) add(root,x,y); 
            else cout<<query(root,x,y)<<endl;
        }
        return 0;
    } 
    

    谢谢,请留下您的👍!!

    相关文章

      网友评论

        本文标题:线段树模板(codvs1080)!

        本文链接:https://www.haomeiwen.com/subject/urkmyftx.html