zoukankan      html  css  js  c++  java
  • 线段树(简单版)

    使用线段树维护一段长度为(n)的区间,线段树的空间要开(4n)的原因

    设要维护的区间为([1, n])长度为(n)

    (n = 2^k, k = 0, 1, 2, 3, ...)

    那么构造的线段树的结点个数不多不少刚好用(2^{k + 1} - 1 < 2n)个, 所以开(2n)足够,此时的线段树为深度为(k + 1)满二叉树。

    (n eq 2^k)那么构造这个线段树所用的结点数(包括浪费的)和一个构造区间长度为(2^t)的区间的线段树所用的结点数相同其中(2^t)为大于n的最小的(2)的次幂。

    下面来求 (t)

    因为(n < 2^t)

    那么(log_{2}n < t)

    所以(t)是大于(log_{2}n)的最小整数

    所以(t = lfloor log_{2}n floor + 1)

    由于构造长度为(2^t)的区间的线段树需要(2^{t + 1} - 1)个结点,即(2^{ lfloor log_{2}n floor + 1 + 1} - 1)个结点, 即(4 * 2^{ lfloor log_{2}n floor} - 1 lt 4n),所以为了保证不越界,开(4n)空间。

    通过浪费一些空间,来让线段树具有完全二叉树的性质(左孩子,右孩子,双亲)(n eq 2^k)时,画出线段树,可以明显看出它不是完全二叉树。

    线段树的层数问题:

    (n = 2^k),最后一层结点数为(n),层数为(log_{2}n + 1)

    (n e 2^k),最后一层结点数为(2^t)(包括空着的),层数为(lfloor log_{2}n floor + 1 + 1)

    所以线段树的层数为(logn)级别。

    线段树的核心操作:

    1. query(root, l, r)
    2. modify(root, idx, val)
    3. build(root, l, r) // 在l,r区间上建立线段树
    4. pushup(root) //更新root结点维护的信息(max, min, ...)
    5. pushdown(带lazy标记的线段树)

    简单版线段树

    1. 单点修改
    2. 区间查询(sum,max,min...)

    原理:用线段树的结点来维护每一段区间,单点修改(递归修改,修改所有相关的区间),区间查询(递归查询返回sum),两者的复杂度(O(logn))

    模板题

    给定 n 个数组成的一个数列,规定有两种操作,一是修改某个元素,二是求子数列[a, b]的连续和。

    输入格式

    第一行包含两个整数 n 和 m,分别表示数的个数和操作次数。

    第二行包含 n 个整数,表示完整数列。

    接下来 m 行,每行包含三个整数 k,a,b (k=0,表示求子数列[a,b]的和;k=1,表示第 a 个数加 b)。

    数列从1开始计数。

    #include<iostream>
    using namespace std;
    
    const int N = 100010;
    
    int a[N];
    int n, m;
    
    struct Node{
        int l, r;
        int sum;
    }tr[N * 4];
    
    
    void pushup(int u){
        tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
    }
    
    void build(int u, int l, int r){
        if(l == r) tr[u] = {l, r, a[l]};
        else{
            tr[u] = {l, r};
            int mid = l + r >> 1;
            build(u << 1, l, mid);
            build(u << 1 | 1, mid + 1, r);
            pushup(u);
        }
    }
    
    int modify(int u, int x, int v){
        if(tr[u].l == tr[u].r) tr[u].sum += v;
        else{
            int mid = tr[u].l + tr[u].r >> 1;
            if(x <= mid) modify(u << 1, x, v);
            else modify(u << 1 | 1, x, v);
            pushup(u);
        }
    }
    
    int query(int u, int l, int r){
        if(tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
        int mid = tr[u].l + tr[u].r >> 1;
        int sum = 0;
        if(l <= mid) sum = query(u << 1, l, r);
        if(r > mid) sum += query(u << 1 | 1, l, r);
        
        return sum;
    }
    
    int main(){
        cin >> n >> m;
        
        for(int i = 1; i <= n; i ++) cin >> a[i];
        
        build(1, 1, n);
        
        while(m --){
            int k, a, b;
            cin >> k >> a >> b;
            
            if(k) modify(1, a, b);
            else cout << query(1, a, b) << endl;
        }
        
        return 0;
    }
    
  • 相关阅读:
    IDEA 启动项目报错 Error:java: java.lang.OutOfMemoryError: GC overhead limit exceeded
    JetBrains 里不为人知的秘密(8) -- 插件篇
    ant-design-vue 之upload 文件上传
    PHP获取IPv4地址
    监控制定程序的CPU和内存开销
    Python字符串转bool函数
    JetsonNano国内环境配置
    局域网主机定时ping实现监控
    无root权限crontab间接实现守护进程
    2020/3/31
  • 原文地址:https://www.cnblogs.com/tomori/p/13720984.html
Copyright © 2011-2022 走看看