• 线段树


    线段树

    一、线段树简介

    1、简单图解及存储结构

    线段树用于维护一段区间的性质,包括最值、和、最大公约数等等

    线段树

    除叶子结点外,是一颗满二叉树 -> 可以用堆来存 -> 可以用一维数组来存

    对编号x的节点:

    • 父节点:x/2向下取整,即:x >> 1
    • 左儿子:2*x,即:x << 1
    • 右儿子:2*x+1,即:x << 1 | 1

    最坏情况下:假设倒数第2层最多有n个节点,除最后一层外一共有2n-1个节点,最后一层最多有2*n个节点,所以一共最多有4n-1个节点。

    2、基本操作及代码(以最大值为例)

    //节点:
    struct Node
    {
        int l, r;
        int v; //区间[l,r]的最大值
    }tr[4 * N];
    //pushup():单点修改,由子节点更新父节点
    void pushup(int u)
    {
        tr[u].v = max(tr[u << 1].v, tr[u << 1 | 1].v);
    }
    //build():初始化线段树
    void build(int u, int l, int r)
    {
        tr[u] = {l, r};
        if(l == r) return;
        int mid = l + r >> 1;
        build(u << 1, l, mid);
        build(u << 1 | 1, mid + 1, r);
    } 
    //modify():单点修改操作
    void modify(int u, int x, int v)
    {
        if(tr[u].l == x && tr[u].r == x) tr[u].v = 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].v;
        
        int mid = tr[u].l + tr[u].r >> 1;
        int v = 0;
        if(l <= mid) v = query(u << 1, l, r);
        if(r > mid) v = max(v, query(u << 1 | 1, l, r));
        return v;
        //if(r <= mid) return query(u << 1, l, r);
        //else if(l > mid) return query(u << 1 | 1, l, r);
        //else return max(query(u << 1, l, r), query(u << 1 | 1, l, r));
    }
    
    //pushdown():区间修改,带懒标记(延迟更新)
    //此处以区间和为例
    //两个信息:sum(区间和)、add(懒标记、区间每个数需要加的值,意义:所有以当前节点为根节点的子树全部加上add,根节点不加)
    void pushdown(int u)
    {
        auto &root = tr[u], &left = tr[u << 1], &right = tr[u << 1 | 1];
        if(root.add)
        {
            left.add += root.add, left.sum += (left.r - left.l + 1) * root.add;
            right.add += root.add, right.sum ++ (right.r - right.l + 1) * root.add;
            root.add = 0;
        }
    }
    pushup()的位置一般在:build()、modify()的结尾
    pushdown()的位置一般在:modify()、query()的开头
        
    

    二、相关题目

    1275.最大数

    1275.最大数

    维护区间最大值的线段树

    245.你能回答这些问题吗

    245.你能回答这些问题吗

    经分析的用线段树维护:区间和、最大前缀和、最大后缀和、最大连续区间和

    246.区间最大公约数

    246.区间最大公约数

    线段树维护:差分数组区间和、区间最大公约数

    243.一个简单的整数问题2

    243.一个简单的整数问题2

    采用带懒标记的线段树解法,实现区间修改,区间查询

    247.亚特兰蒂斯

    247.亚特兰蒂斯

    用线段树维护扫描线,根据该题的特殊性质可推出带懒标记的线段树不需要pushdown操作

    让人自闭的题目,裂开。。。。

    1277.维护序列

    1277.维护序列

    带两个懒标记的线段树

  • 相关阅读:
    使用Ansible连接AWS EC2
    centos7 常用工具包安装
    基于redis的分布式ID生成器
    使用Docker的macvlan为容器提供桥接网络及跨主机通讯
    小程序使用腾讯视频
    切换 Python2 Python3
    PHP字符串替换
    小程序常用操作,if,for,跳转,弹出提示
    小程序订单核销的思路
    PHP 批量删除的实现
  • 原文地址:https://www.cnblogs.com/grain-rain/p/14302624.html
Copyright © 2020-2023  润新知