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

    1、什么是线段树?

    线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。

    使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N,实际应用时一般还要开4N的数组以免越界,因此有时需要离散化让空间压缩

    2、构建线段树

    通过像二叉树一样递归建树,但需要记录做边界和右边界。

    3、lazy

    即懒标记,我们的加减、乘除都是区间修改操作,如果将线段树拆开在每个叶子结点上面进行修改再维护,最坏情况下我们修改的复杂度就变成了O(mnlogn),十分慢。于是我们引入懒标记。每一个线段树的结点都会有一个加法的标记和乘法的标记,如果我们想要修改的区间覆盖了结点的对应区间,我们不对其中的每一个元素进行修改,而是只更新两个标记和总值,这样就巧妙地优化了程序的复杂度。

    4、区间修改

    判断要进行修改的区间是不是包括了这个结点的区间,如果包括了,就直接修改这个点的两个标记和总值。如果没有包括,就往这个节点的孩子递归,直到找到被包括的区间进行修改。进行修改时,为了方便,可以先把加法标记进行相乘,然后使用乘法标记,简单来说,就是(sum+lazy_+)*lazy_x=sum*lazy_x+lazy_+*lazy_x。

    5、区间查询

    思路和区间修改相类似,从第一个结点开始 ,如果结点被覆盖,就返回它维护的区间和sum。

    代码

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    long long n,m,k,x,y;
    long long a[100000];
    
    struct tree{
        long long l,r,sum,tag;
    }t[400000];
    
    
    long long ls(long long rt){return rt << 1;}
    long long rs(long long rt){return rt << 1 | 1;}
    
    void build(long long rt,long long l,long long r){
        t[rt].l = l,t[rt].r = r;
        if(l == r){
            t[rt].sum = a[l];
            return;
        }
        long long mid = (l + r) >> 1;
        build(ls(rt),l,mid);
        build(rs(rt),mid+1,r);
        t[rt].sum = t[ls(rt)].sum + t[rs(rt)].sum;
    }
    
    //root
    
    void push_down(long long rt){
        t[ls(rt)].sum += t[rt].tag * (t[ls(rt)].r - t[ls(rt)].l + 1);
        t[rs(rt)].sum += t[rt].tag * (t[rs(rt)].r - t[rs(rt)].l + 1);
    
        t[ls(rt)].tag += t[rt].tag;
        t[rs(rt)].tag += t[rt].tag;
    
        t[rt].tag = 0;
    }
    
    void change(long long rt,long long l,long long r,long long x){
        if(l <= t[rt].l && r >= t[rt].r){
            t[rt].sum += (t[rt].r - t[rt].l + 1) * x;
            t[rt].tag += x;
            return;
        }
    
        if(t[rt].tag) push_down(rt);
    
        if(l <= t[ls(rt)].r){
            change(ls(rt),l,r,x);
        }
        if(r >= t[rs(rt)].l){
            change(rs(rt),l,r,x);
        }
    
        t[rt].sum = t[ls(rt)].sum + t[rs(rt)].sum;
    }
    
    long long check(long long rt,long long l,long long r){
        if(l <= t[rt].l && r >= t[rt].r){
            return t[rt].sum;
        }
    
        if(t[rt].tag) push_down(rt);
    
        long long res = 0;
    
        if(l <= t[ls(rt)].r){
            res += check(ls(rt),l,r);
        }
        if(r >= t[rs(rt)].l){
            res += check(rs(rt),l,r);
        }
        return res;
    }
    
    int main(){
        cin >> n >> m;
        for(long long i = 1;i <= n; i++) cin >> a[i];
        build(1,1,n);
    
        for(long long i = 1;i <= m; i++){
            cin >> k;
            if(k == 1){
                cin >> x >> y >> k;
                change(1,x,y,k);
            }else{
                cin >> x >> y;
                cout << check(1,x,y) << endl;
            }
        }
        return 0;
    }

    借鉴大佬:https://www.cnblogs.com/Cao-Yucong/p/12164576.html

  • 相关阅读:
    Extjs4.0以上版本 Ext.Ajax.request请求的返回问题
    C# NPOI 操作Excel 案例
    C# Microsoft.Office 操作Excel总结
    asp.net core NLog将日志写到文件
    新装的SSMS一打开就显示VS许可证过期,但VS又运行正常,解决方法。
    sql server 查询log日志 sql语句
    sql server 删除所有表及所有存储过程、所有视图和递归查询、数字类型转为字符串
    C#使用Selenium+PhantomJS抓取数据
    python爬虫实例项目大全
    SQL Server TVPs 批量插入数据
  • 原文地址:https://www.cnblogs.com/zhaoxuelin/p/12912696.html
Copyright © 2011-2022 走看看