线段树(原理+模板)

本文来源于大佬的讲解:

1:线段树从0开始:https://blog.csdn.net/zearot/article/details/52280189

2:详解:https://www.cnblogs.com/AC-King/p/7789013.html(包含扫描线,可持久化(主席树))

综述

假设有编号从1到n的n个点,每个点都存了一些信息,用[L,R]表示下标从L到R的这些点。

线段树的用处就是,对编号连续的一些点进行修改或者统计操作,修改和统计的复杂度都是O(log2(n)).

线段树的原理,就是,将[1,n]分解成若干特定的子区间(数量不超过4*n),然后,将每个区间[L,R]都分解为

少量特定的子区间,通过对这些少量子区间的修改或者统计,来实现快速对[L,R]的修改或者统计。

由此看出,用线段树统计的东西,必须符合区间加法,否则,不可能通过分成的子区间来得到[L,R]的统计结果。

符合区间加法的例子:

数字之和——总数字之和 = 左区间数字之和 + 右区间数字之和

最大公因数(GCD)——总GCD = gcd( 左区间GCD , 右区间GCD );

最大值——总最大值=max(左区间最大值,右区间最大值)

不符合区间加法的例子:

众数——只知道左右区间的众数,没法求总区间的众数

01序列的最长连续零——只知道左右区间的最长连续零,没法知道总的最长连续零

 

一个问题,只要能化成对一些连续点的修改和统计问题,基本就可以用线段树来解决了,由于点的信息可以千变万化,所以线段树是一种非常灵活的数据结构,可以做的题的类型特别多,只要会转化。

线段树当然是可以维护线段信息的,因为线段信息也是可以转换成用点来表达的(每个点代表一条线段)。

模板1:

单点更新,单点查询:

int a[maxn];
int ans_min,ans_max,ans_sum;
struct node{
    int l,r;
    int maxx,minx,sum;
}tree[maxn<<2];
void build(int o,int l,int r)
{
    tree[o].l = l;
    tree[o].r = r;
    if(l == r){
        tree[o].maxx = tree[o].minx = tree[o].sum = a[l];
        return ;
    }
    int m = MID(l,r);
    int  lc = lson(o),rc = rson(o);
    build(lc,l,m);
    build(rc,m+1,r);
    tree[o].maxx = max(tree[lc].maxx, tree[rc].maxx);
    tree[o].minx = min(tree[lc].minx, tree[rc].minx);
    tree[o].sum = tree[lc].sum + tree[rc].sum;
}
void update(int o,int p,int v)
{
    if(tree[o].l == tree[o].r){
        tree[o].maxx = v;  //视情况而定
        tree[o].minx = v;
        tree[o].sum = v;
        return ;
    }
    int m = MID(tree[o].l,tree[o].r);
    int lc = lson(o),rc = rson(o);
    if(p<=m) update(lc,p,v);
    else update(rc,p,v);
    tree[o].maxx = max(tree[lc].maxx, tree[rc].maxx);
    tree[o].minx = min(tree[lc].minx, tree[rc].minx);
    tree[o].sum = tree[lc].sum + tree[rc].sum;
}
void query_init()//查询前,将全局变量初始化
{
    ans_max = -INF;
    ans_min = INF;
    ans_sum = 0;
}
void query(int o,int l,int r)
{
    if(tree[o].l == l && tree[o].r == r){
        ans_max = max(ans_max, tree[o].maxx);
        ans_min = min(ans_min, tree[o].minx);
        ans_sum += tree[o].sum;
        return;
    }
    int m = MID(tree[o].l,tree[o].r);
    int lc = lson(o),rc = rson(o);
    if(r<=m) query(lc,l,r);
    else if(l>m) query(rc,l,r);
    else{
        query(lc,l,m);
        query(rc,m+1,r);
    }
}

模板2:

区间更新,区间查询:

LL a[maxn];
struct node{
    int l,r;
    LL sum,inc;
}tree[maxn<<2];
void build(int o,int l,int r)
{
    tree[o].l = l;
    tree[o].r = r;
    tree[o].inc = 0;
    if(l == r){
        tree[o].sum = a[l];
        return ;
    }
    int m = MID(l,r);
    int  lc = lson(o),rc = rson(o);
    build(lc,l,m);
    build(rc,m+1,r);
    tree[o].sum = tree[lc].sum+tree[rc].sum;
}
void update(int o,int l,int r,LL val)
{
    if(tree[o].l == l && tree[o].r == r){
        tree[o].inc += val;
        return ;
    }
    tree[o].sum += val*(r-l+1);
    int m = MID(tree[o].l,tree[o].r);
    int lc = lson(o),rc = rson(o);
    if(r<=m) update(lc,l,r,val);
    else if(l>m) update(rc,l,r,val);
    else{
        update(lc,l,m,val);
        update(rc,m+1,r,val);
    }
}
LL ans_sum;
LL query(int o,int l,int r)
{
    if(tree[o].l == l && tree[o].r == r){
        return tree[o].sum + tree[o].inc *(r-l+1);
    }
    tree[o].sum += (tree[o].r-tree[o].l+1)*tree[o].inc;
    int m = MID(tree[o].l,tree[o].r);
    int lc = lson(o),rc = rson(o);
    update(lc,tree[o].l,m,tree[o].inc);
    update(rc,m+1,tree[o].r,tree[o].inc);  //只更新到下一层
    tree[o].inc = 0;  //清除标记
    if(r<=m) ans_sum = query(lc,l,r);
    else if(l>m) ans_sum = query(rc,l,r);
    else{
        ans_sum = query(lc,l,m)+query(rc,m+1,r);
    }
    return ans_sum;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值