线段树模板

好文分享:【数据结构】线段树(Segment Tree) - 小仙女本仙 - 博客园

线段树和树状数组的基本功能都是在某一满足结合律的操作(比如加法,乘法,最大值,最小值)下,O(logn)的时间复杂度内修改单个元素并且维护区间信息。

不同的是,树状数组只能维护前缀“操作和”(前缀和,前缀积,前缀最大最小),而线段树可以维护区间操作和。

线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N,实际应用时一般还要开4N4N的数组以免越界,因此有时需要离散化让空间压缩。

线段树操作:1.单点修改       

只要不断修改这个区间及其父区间即可,而不会影响其他区间情况,时间复杂度O(logn)

2.区间查询(最小值,最小值出现次数等某些)

比如要查询【2,5】这个区间,我们看看能不能将这个区间拆成线段树上的若干段区间。从根节点开始看,【2,5】与【1,7】没有什么关系,所以往下看,查询【2,5】与【1,4】的交【2,4】,查询【2,5】与【5,7】的交【5,5】,然后再继续往下递归,直到有满足的区间或者有孤立的节点了,然后再不断返回即可

例题:P3372 【模板】线段树 1

本题要进行区间加 和 区间查询操作, 其实这个树状数组改一改也能做,开两个树状数组就行了,这里采用线段树操作, 直接上板子。


#include <bits/stdc++.h>
using namespace std;
#define ll long long
typedef pair<int, int> PII;
#define pb push_back
const int N = 2E5 + 5;
int n, q, a[N];
const int mod = 1e9 + 7;
 
struct tag {
    ll mul, add;
};
tag operator + (const tag &t1, const tag &t2) {
    // (mul1 + add1) * mul2 + add2;
    return {t1.mul * t2.mul , (t1.add * t2.mul % mod + t2.add)}; 
}
struct node {
    tag t;
    ll val;
    int sz;
}seg[N * 4];
 
 
void update(int id) {
    seg[id].val = (seg[id * 2].val + seg[id * 2 + 1].val);
}
void build(int id, int l, int r) {
    seg[id].t = (tag){1, 0};
    seg[id].sz = r - l + 1;
    if(l == r){
        seg[id].val = a[l];
    } else {
        int mid = (l + r) / 2;
        build(id * 2, l, mid);
        build(id * 2 + 1, mid + 1, r);
        update(id);
    }
}
void settag(int id, tag t) {
    seg[id].val = seg[id].val * t.mul + seg[id].sz * t.add;
    seg[id].t = seg[id].t + t;
}
 
void pushdown(int id) {
    if(seg[id].t.mul != 1 || seg[id].t.add != 0) {
        settag(id * 2, seg[id].t);
        settag(id * 2 + 1, seg[id].t);
        seg[id].t.mul = 1;
        seg[id].t.add = 0;
    }
}
void modify(int id, int l, int r, int ql, int qr, tag t) {
    if(l == ql && r == qr) {
        settag(id, t);
        return;
    }
    pushdown(id);
    int mid = (l + r) / 2;
    if(qr <= mid) modify(id * 2, l, mid, ql, qr, t);
    else if(ql > mid) modify(id * 2 + 1, mid + 1, r, ql, qr, t);
    else modify(id * 2, l, mid, ql, mid, t),
        modify(id * 2 + 1, mid + 1, r, mid + 1, qr, t);
    update(id);
}
 
ll query(int id, int l, int r, int ql, int qr) {
    if(l == ql && r == qr) {
        return seg[id].val;
    }
    pushdown(id);
    int mid = (l + r) / 2;
    if(qr <= mid) return query(id * 2, l, mid, ql, qr);
    else if(ql > mid) return query(id * 2 + 1, mid + 1, r, ql, qr);
    else return (query(id * 2, l, mid, ql, mid) +
        query(id * 2 + 1, mid + 1, r, mid + 1, qr));
}
int main(){
    scanf("%d %d", &n, &q);
    for(int i = 1; i <= n; ++i)
        scanf("%d", &a[i]);
    build(1, 1, n);
    while(q--) {
        int ty; scanf("%d", &ty);
        if(ty == 1) {
            int l, r, d;
            scanf("%d %d %d", &l, &r, &d);
            modify(1, 1, n, l, r, (tag){1, d});
        } else {
            int l, r; scanf("%d %d", &l, &r);
            printf("%lld\n", query(1, 1, n, l, r));
        }
    }
 
    return 0;
}

例题2:P3373 【模板】线段树 2

这里看到需要好多操作, 我们肯定要利用标记去做这些操作, 我们可以让标记记录 + 和 * 两种操作, 然后将所有的修改变成这俩个操作。 如:

  • 将某区间每一个数乘上 x,那么就是*x + 0,

  • 将某区间每一个数加上 x,  那么就是*0 + x;

  • 求出某区间每一个数的和,  直接无脑query

  • 
    #include <bits/stdc++.h>
    using namespace std;
    #define ll long long
    typedef pair<int, int> PII;
    #define pb push_back
    const int N = 2E5 + 5;
    int n, q, a[N];
    const int mod = 1e9 + 7;
     
    struct tag {
        ll mul, add;
    };
    tag operator + (const tag &t1, const tag &t2) {
        // (mul1 + add1) * mul2 + add2;
        return {t1.mul * t2.mul, (t1.add * t2.mul + t2.add)}; 
    }
    struct node {
        tag t;
        ll val;
        int sz;
    }seg[N * 4];
     
     
    void update(int id) {
        seg[id].val = (seg[id * 2].val + seg[id * 2 + 1].val);
    }
    void build(int id, int l, int r) {
        seg[id].t = (tag){1, 0};
        seg[id].sz = r - l + 1;
        if(l == r){
            seg[id].val = a[l];
        } else {
            int mid = (l + r) / 2;
            build(id * 2, l, mid);
            build(id * 2 + 1, mid + 1, r);
            update(id);
        }
    }
    void settag(int id, tag t) {
        seg[id].val = seg[id].val * t.mul + seg[id].sz * t.add;
        seg[id].t = seg[id].t + t;
    }
     
    void pushdown(int id) {
        if(seg[id].t.mul != 1 || seg[id].t.add != 0) {
            settag(id * 2, seg[id].t);
            settag(id * 2 + 1, seg[id].t);
            seg[id].t.mul = 1;
            seg[id].t.add = 0;
        }
    }
    void modify(int id, int l, int r, int ql, int qr, tag t) {
        if(l == ql && r == qr) {
            settag(id, t);
            return;
        }
        pushdown(id);
        int mid = (l + r) / 2;
        if(qr <= mid) modify(id * 2, l, mid, ql, qr, t);
        else if(ql > mid) modify(id * 2 + 1, mid + 1, r, ql, qr, t);
        else modify(id * 2, l, mid, ql, mid, t),
            modify(id * 2 + 1, mid + 1, r, mid + 1, qr, t);
        update(id);
    }
     
    ll query(int id, int l, int r, int ql, int qr) {
        if(l == ql && r == qr) {
            return seg[id].val;
        }
        pushdown(id);
        int mid = (l + r) / 2;
        if(qr <= mid) return query(id * 2, l, mid, ql, qr);
        else if(ql > mid) return query(id * 2 + 1, mid + 1, r, ql, qr);
        else return (query(id * 2, l, mid, ql, mid) +
            query(id * 2 + 1, mid + 1, r, mid + 1, qr));
    }
    int main(){
        scanf("%d %d", &n, &q);
    
        for(int i = 1; i <= n; ++i)
            scanf("%d", &a[i]);
        build(1, 1, n);
        while(q--) {
            int ty; scanf("%d", &ty);
            if(ty <= 3) {
                int l, r, d;
                scanf("%d %d %d", &l, &r, &d);
                if(ty == 1) modify(1, 1, n, l, r, (tag){1, d});
                else if (ty == 2) modify(1, 1, n, l, r, (tag){d, 0});
                else modify(1, 1, n, l, r, (tag){0, d});
            } else {
                int l, r; scanf("%d %d", &l, &r);
                printf("%lld\n", query(1, 1, n, l, r));
            }
        }
     
        return 0;
    }

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ACM线段树模板C ```c #include<bits/stdc++.h> using namespace std; const int N=1e5+5;//数组开大一点 int n,m; int a[N]; struct node{ int l,r;//左右端点 int sum,lazy;//区间和和懒标记 }t[N*4]; void up(int p){//向上更新 t[p].sum=t[p<<1].sum+t[p<<1|1].sum; } void down(int p){//向下更新 if(t[p].lazy){ t[p<<1].lazy+=t[p].lazy; t[p<<1|1].lazy+=t[p].lazy; t[p<<1].sum+=t[p].lazy*(t[p<<1].r-t[p<<1].l+1); t[p<<1|1].sum+=t[p].lazy*(t[p<<1|1].r-t[p<<1|1].l+1); t[p].lazy=0; } } void build(int p,int l,int r){//建树 t[p].l=l,t[p].r=r; if(l==r){ t[p].sum=a[l]; return; } int mid=(l+r)/2; build(p<<1,l,mid); build(p<<1|1,mid+1,r); up(p); } void change(int p,int l,int r,int k){//单点修改 if(t[p].l==t[p].r){ t[p].sum=k; return; } down(p); int mid=(t[p].l+t[p].r)/2; if(r<=mid) change(p<<1,l,r,k); else if(l>mid) change(p<<1|1,l,r,k); else{ change(p<<1,l,mid,k); change(p<<1|1,mid+1,r,k); } up(p); } void add(int p,int l,int r,int k){//区间修改 if(t[p].l>=l&&t[p].r<=r){ t[p].sum+=k*(t[p].r-t[p].l+1); t[p].lazy+=k; return; } down(p); int mid=(t[p].l+t[p].r)/2; if(l<=mid) add(p<<1,l,r,k); if(r>mid) add(p<<1|1,l,r,k); up(p); } int query(int p,int l,int r){//区间查询 if(t[p].l>=l&&t[p].r<=r) return t[p].sum; down(p); int mid=(t[p].l+t[p].r)/2,ans=0; if(l<=mid) ans+=query(p<<1,l,r); if(r>mid) ans+=query(p<<1|1,l,r); return ans; } int main(){ cin>>n>>m; for(int i=1;i<=n;i++) cin>>a[i]; build(1,1,n); for(int i=1;i<=m;i++){ int opt,x,y,k; cin>>opt; if(opt==1){ cin>>x>>y>>k; add(1,x,y,k); } if(opt==2){ cin>>x>>y; cout<<query(1,x,y)<<endl; } if(opt==3){ cin>>x>>k; change(1,x,x,k); } } return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值