线段树原理

本文来源于大佬的讲解:

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:建树

#define MID(l, r) ((l) + ((r) - (l)) / 2)
#define lson(o) ((o)<<1)
#define rson(o) ((o)<<1|1)
int a[maxn];
struct node
{
    int l, r, mmax, mmin, sum;
}tree[maxn];
void build(int o, int l, int r)
{
    tree[o].l = l, tree[o].r = r;
    if(l == r)
    {
        tree[o].mmax = tree[o].mmin = 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].mmax = max(tree[lc].mmax, tree[rc].mmax);
    tree[o].mmin = min(tree[lc].mmin, tree[rc].mmin);
    tree[o].sum = tree[lc].sum + tree[rc].sum;
}

 

2:查询区间[ql, qr]中的max,min,sum

int ql, qr;//查询区间[ql, qr]中的max,min,sum
int ans_max, ans_min, ans_sum;
void query_init()//查询前,将全局变量初始化
{
    ans_max = -INF;
    ans_min = INF;
    ans_sum = 0;
}
void query(int o)
{
    if(ql <= tree[o].l && qr >= tree[o].r)//[L, R]包含在[ql, qr]区间内,直接用该节点的信息,达到线段树查询快的操作
    {
        ans_max = max(ans_max, tree[o].mmax);
        ans_min = min(ans_min, tree[o].mmin);
        ans_sum += tree[o].sum;
        return;
    }
    int m = MID(tree[o].l, tree[o].r);
    if(ql <= m)query(lson(o));
    if(qr > m)query(rson(o));
}

 

3:单点更新,a[p] += v

如果需要更新成a[p]  = v,把下面的+=换成=即可

 

 

//单点更新,a[p] += v;
int p, v;
void update(int o)
{
    if(tree[o].l == tree[o].r)
    {
        tree[o].mmax += v;
        tree[o].mmin += 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);
    else update(rc);
    tree[o].mmax = max(tree[lc].mmax, tree[rc].mmax);
    tree[o].mmin = min(tree[lc].mmin, tree[rc].mmin);
    tree[o].sum = tree[lc].sum + tree[rc].sum;
}

 

4:综合(注意要开4倍空间)

#define MID(l, r) ((l) + ((r) - (l)) / 2)
#define lson(o) ((o)<<1)
#define rson(o) ((o)<<1|1) 
int a[maxn];
struct node
{
    int l, r, mmax, mmin, sum;
}tree[maxn];
void build(int o, int l, int r)
{
    tree[o].l = l, tree[o].r = r;
    if(l == r)
    {
        tree[o].mmax = tree[o].mmin = 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].mmax = max(tree[lc].mmax, tree[rc].mmax);
    tree[o].mmin = min(tree[lc].mmin, tree[rc].mmin);
    tree[o].sum = tree[lc].sum + tree[rc].sum;
}
int ql, qr;//查询区间[ql, qr]中的max,min,sum
int ans_max, ans_min, ans_sum;
void query_init()//查询前,将全局变量初始化
{
    ans_max = -INF;
    ans_min = INF;
    ans_sum = 0;
}
void query(int o)
{
    if(ql <= tree[o].l && qr >= tree[o].r)//[L, R]包含在[ql, qr]区间内,直接用该节点的信息,达到线段树查询快的操作
    {
        ans_max = max(ans_max, tree[o].mmax);
        ans_min = min(ans_min, tree[o].mmin);
        ans_sum += tree[o].sum;
        return;
    }
    int m = MID(tree[o].l, tree[o].r);
    if(ql <= m)query(lson(o));
    if(qr > m)query(rson(o));
}
//单点更新,a[p] += v;
int p, v;
void update(int o)
{
    if(tree[o].l == tree[o].r)
    {
        tree[o].mmax += v;
        tree[o].mmin += 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);
    else update(rc);
    tree[o].mmax = max(tree[lc].mmax, tree[rc].mmax);
    tree[o].mmin = min(tree[lc].mmin, tree[rc].mmin);
    tree[o].sum = tree[lc].sum + tree[rc].sum;
}

 

区间更新,区间查询:

struct node
{
    int l,r;
    LL sum,inc; //inc:区间增量的累加
}tree[maxn*4];
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 a,int b,LL c)
{
    if(tree[o].l == a && tree[o].r == b){
        tree[o].inc += c;
        return ;
    }
    tree[o].sum += c*(b-a+1);
    int m = MID(tree[o].l,tree[o].r);
    int lc = lson(o),rc = rson(o);
    if(b<=m) update(lc,a,b,c);
    else if(a>m) update(rc,a,b,c);
    else{
        update(lc,a,m,c);
        update(rc,m+1,b,c);
    }
}
//区间查询
LL query(int o,int a,int b)
{
    if(tree[o].l==a && tree[o].r==b){
        return tree[o].sum + (b-a+1)*tree[o].inc;
    }
    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(b<=m) return query(lc,a,b);
    else if(a>m)  return query(rc,a,b);
    else return query(lc,a,m)+query(rc,m+1,b);
}

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值