zoukankan      html  css  js  c++  java
  • 线段树模板 求区间和, 区间加法,乘法更新

    今天再次入门线段树

    有了一点点感觉

    线段树在有结合律性质的区间操作都可以用也许

    然而我并不会

    
    #include<bits/stdc++.h>
    
    #define ll long long
    #define INF 0x3f3f3f3f
    #define FOR(i,n)    for(int i = 1 ; (i)<=(n);++(i))
    #define lson(i) (i<<1)
    #define rson(i) ((i<<1)|1)
    using namespace std;
    
    const int maxn = 500010+10;
    ll a[maxn];
    ll modp;
    struct node{
        ll l,r;
        ll val,tag;
        ll mut  ;
    }seg[maxn*4];
    
    // 儿子更新父亲
    void push_up(ll i){
        seg[i].val = (seg[lson(i)].val+seg[lson(i)|1].val)%modp;
    }
    // 建树
    void build(ll i,ll l,ll r){
        seg[i].l = l;
        seg[i].r = r;
        seg[i].mut = 1;
        if(l==r){
            seg[i].val = a[l]%modp;
            return;
        }
        ll mid = (l+r)>>1;
        build(lson(i),l,mid);
        build(rson(i),mid+1,r);
        push_up(i);
    }
    // 下发懒标记
    void push_down(ll p){
        if(seg[p].mut!=1){  //乘法标记
            seg[lson(p)].val = (seg[p].mut*seg[lson(p)].val)%modp;
            seg[rson(p)].val = (seg[p].mut*seg[rson(p)].val)%modp;
    
            seg[lson(p)].mut = (seg[p].mut*seg[lson(p)].mut)%modp;
            seg[rson(p)].mut = (seg[p].mut*seg[rson(p)].mut)%modp;
    
            seg[(lson(p))].tag = (seg[p].mut*seg[lson(p)].tag)%modp;
            seg[rson(p)].tag = (seg[p].mut*seg[rson(p)].tag)%modp;
    
            seg[p].mut = 1;
        }
        if(seg[p].tag){    //加法标记
            seg[lson(p)].val =(seg[lson(p)].val+seg[p].tag*(seg[lson(p)].r - seg[lson(p)].l+1))%modp;
            seg[rson(p)].val =(seg[rson(p)].val+seg[p].tag*(seg[rson(p)].r - seg[rson(p)].l+1))%modp;
            seg[lson(p)].tag = (seg[lson(p)].tag+seg[p].tag)%modp;
            seg[rson(p)].tag = (seg[rson(p)].tag+seg[p].tag)%modp;
            seg[p].tag = 0;
        }
    }
    // 加法更新
    void update(ll i,ll l,ll r,ll val){
        if(seg[i].l >= l && seg[i].r <= r){ // 覆盖区间 直接更新
            seg[i].tag = (val+seg[i].tag)%modp; 
            seg[i].val = (val*(seg[i].r-seg[i].l+1)+seg[i].val)%modp;
            return;
        }
        if(seg[i].mut!=1 || seg[i].tag)
            push_down(i);
        ll mid = (seg[i].l + seg[i].r) >> 1;
        if(l<=mid)  update(lson(i),l,r,val);
        if(r>mid) update(rson(i),l,r,val);
        push_up(i);
    }
    // 乘法更新
    void updateMut(ll i,ll l,ll r,ll mut){
        if(seg[i].l >= l && seg[i].r <= r){ // 覆盖区间 直接更新
            seg[i].mut = (mut*seg[i].mut)%modp;
            seg[i].tag = (mut*seg[i].tag)%modp;
            seg[i].val = (mut*seg[i].val)%modp;
            return;
        }
        if(seg[i].mut!=1 || seg[i].tag)
            push_down(i);
        ll mid = (seg[i].l + seg[i].r) >> 1;
        if(l<=mid)  updateMut(lson(i),l,r,mut);
        if(r>mid)   updateMut(rson(i),l,r,mut);
        push_up(i);
    }
    // 查询
    ll query(ll i,ll l,ll r){
        if(seg[i].l >= l &&seg[i].r <= r){
            return seg[i].val%modp;
        }
        if(seg[i].mut!=1 || seg[i].tag)
            push_down(i);
        ll mid = (seg[i].l + seg[i].r) >> 1;
        ll res = 0;
        if(l<=mid)  res+= query(lson(i),l,r);
        if(r>mid)   res+= query(rson(i),l,r);
        return res%modp;
    }
    
    ll n,m;
    
    int main(){
        cin >>n >> m >>modp;
        FOR(i,n)cin >>a[i];
        build(1,1,n);
        ll op,x,y;
        ll k;
        FOR(i,m){
            cin >> op >>x >> y;
            if(op==1){
                cin >>k;
                updateMut(1,x,y,k);
            }else if(op==2){
                cin >> k;
                update(1,x,y,k);
            }else{
                cout << query(1,x,y)%modp <<endl;
            }
        }
    
        return 0;
    }
    

    结构体比数组更加直观一点,调用的时候也不用传那么多参数,就是引用成员代码有点长(然后右儿子写成了左儿子完全看不出来,wa了半小时QAQ)

    总结下线段树的特点

    • 每个节点都代表了一个区间的信息(长度为1的区间为叶子节点,代表一个点),可以方便的进行区间操作
    • 树的结构为完全二叉树,递归的性质很好,也有左右儿子的性质
    • 区间更新时的懒标记,当前区间被更新区间所覆盖,则直接在当前区间更新打上懒标记后返回,子区间只会在对其操作时通过懒标记再更新。
    • 多个操作要注意之间的影响(乘法对加法有影响,要更新加法的懒标记)
  • 相关阅读:
    华为超大云数据中心落地贵州,这些硬核技术有利支撑“东数西算”
    在高并发环境下该如何构建应用级缓存
    使用 Python Poetry 进行依赖管理
    AI新手语音入门:认识词错率WER与字错率CER
    一文带你了解什么是GitOps
    需求蔓延,常见但不正常,教你如何破
    云图说|初识ModelArts开发者生态社区——AI Gallery
    XML学习笔记:关于字符编码的理解~
    Python中单引号、双引号和三双引号的区别:
    LBFGS算法的使用~
  • 原文地址:https://www.cnblogs.com/xxrlz/p/10392812.html
Copyright © 2011-2022 走看看