线段树

线段树

线段树的建树,更新点、区间,查找点、区间

//左子树 u << 1, 右子树 u << 1 | 1
//建树或遍历核心是判断 mid 和 l,r 的关系
//是大区间包容小区间,优先遍历大区间
// lazy_tag 用作处理记录器,用到就处理,没用到只处理总节点,节省时间
//考虑最坏情况要开 N * 4 给线段树数组
//此代码演示区间最大值
//建树为一颗完全二叉树
//建树O(n) 其他操作一般为O(log n)

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>

using namespace std;

const int N = 100010;

int a[N]; //原数组
struct tree{
    int l,r;
    int v;
} t[N * 4];

//每次改变数值后都要调用更新树
void push_up(int k){
    t[k].v = max(t[k << 1].v,t[k << 1 | 1].v);
}

//建树
void build(int u,int l,int r){
    t[u] = {l,r};

    if(l == r) t[u].v = a[l];
    else{
        int mid = l + r >> 1;

        build(u << 1,l,mid);
        build(u << 1 | 1,mid + 1,r);

        push_up(u);
    }
}

// O(logn) 更新点
void upd_point(int x,int v,int u){
    if(t[u].l == x && t[u].r == x) t[u].v += v;
    else{
        int mid = t[u].l + t[u].r >> 1;

        //判断在左子树还是右子树
        if(x <= mid) upd_point(x,v,u << 1);
        else upd_point(x,v,u << 1 | 1);

        push_up(u);
    }
}

//区间查询或更新要保持递归期间区间端点!!!不改变!!!
//改变的是递归左子树或者右子树

//区间查询
int query(int u,int l,int r){
    if(l <= t[u].l && t[u].r <= r) return t[u].v;

    //lazy_chk(u);  如果使用了lazy数组

    int mid = t[u].l + t[u].r >> 1;

    int res = 0;
    if(l <= mid) res = query(u << 1,l,r);
    if(mid < r) res = max(res,query(u << 1 | 1,l,r));

    return res;
}

int lazy[N * 4];

//区间改变,利用lazy_tag减少时间损耗  nlogn -> logn
void lazy_chk(int u){
    if(lazy[u]){
        lazy[u << 1] += lazy[u];
        lazy[u << 1 | 1] += lazy[u];
        t[u << 1].v += lazy[u];
        t[u << 1 | 1].v += lazy[u];
        lazy[u] = 0;
    }
}

// l <= mid 与左子树有交集
// mid < r 与右子树有交集

//有区间变化的区间查询
int query_lazy(int u,int l,int r){
    if(l <= t[u].l && t[u].r <= r) return t[u].v;

    lazy_chk(u);  //如果使用了lazy数组

    int mid = t[u].l + t[u].r >> 1;

    int res = 0;
    if(l <= mid) res = query_lazy(u << 1,l,r);
    if(mid < r) res = max(res,query_lazy(u << 1 | 1,l,r));

    return res;
}

//区间更新
void upd(int u,int v,int l,int r){
    if(l <= t[u].l && t[u].r <= r) lazy[u] += v,t[u].v += v;
    else{
        lazy_chk(u);    //检查要不要被lazy更新

        int mid = t[u].l + t[u].r >> 1;

        if(l <= mid) upd(u << 1,v,l,r);
        if(mid < r) upd(u << 1 | 1,v,l,r);

        push_up(u);     //改变数据要更新树
    }
}

int main(){
    int n;
    cin >> n;
    for(int i = 1;i <= n;i ++) a[i] = i;

    build(1,1,n);

    for(int i = 1;i <= n;i ++) cout << query(1,i,i) << ' ';
    cout << endl << "建树" << endl;

    upd_point(2,2,1);

    for(int i = 1;i <= n;i ++) cout << query(1,i,i) << ' ';
    cout << endl << "更新点" << endl;

    upd(1,1,1,n);
    for(int i = 1;i <= n;i ++) cout << query_lazy(1,i,i) << ' ';
    cout << endl << "更新区间" << endl;
}

cite:
1 2 3

posted @ 2021-10-15 21:33  Xuuxxi  阅读(42)  评论(0)    收藏  举报