数据结构-模板-线段树

线段树我闭着眼都能打
这里先是废话。线段树是一个有些整人心态的数据结构,,,以后应该会更多我应该算是理解,但这是在心里默认线段树是绝对正确的情况下,按照脑补去理解。至今我的脑部还未被推翻所以我就认为是对的,虽然很不严谨就是了。但毕竟我也就一普通人。
好了,接下来谈谈线段树。线段树能够以O(n)时间构造。每次操作,包括但不仅限于单点查询,单点修改,区间查询,区间修改,可以在O(logn)的时间内处理。
是个平衡二叉树很好理解(大概?),也正是因为它是平衡二叉树,所以它单点修改和单点查询和区间查询可以在O(logn)内完成,但是对于区间修改需要依赖于lazytag数组来缩短至O(logn),想想极限情况下修改[1,n]范围内的数据。
接下来直接放上线段树维护区间和的代码,修改操作仅限于区间加。
对应洛谷的题:https://www.luogu.com.cn/problem/P3372?contestId=43385

#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
typedef long long ll;
typedef unsigned long long ull;
const ll MAXN=1e18;
const int MOD=1e6;
 
struct SegmentTree{
    ll date[400050];
    ll tag[400050];
    #define lson rt<<1  //左孩子
    #define rson rt<<1|1 //右孩子
    void push_up(int rt){
        date[rt]=date[lson]+date[rson]; //更新date[rt]
    }
    //依靠push_down函数实现lazy_tag的向下传递。
    void push_down(int rt,int lef,int rig){
        if(tag[rt]){
            int mid=(lef+rig)>>1;
            tag[lson]+=tag[rt];
            tag[rson]+=tag[rt];
            date[lson]+=(mid-lef+1)*tag[rt];
            date[rson]+=(rig-mid)*tag[rt];
            tag[rt]=0;
        }
    }
    //构造树
    void init(int lef,int rig,int rt){
        if(lef==rig) {cin>>date[rt];return;}
        int mid=(lef+rig)>>1;
        init(lef,mid,lson);
        init(mid+1,rig,rson);
        push_up(rt);
    }
    //区间更新,区间加的方法和lazy_tag的传递是相互独立的。直到需要修改的最底层才合并它们
    //a,b时修改的区间,c是增加的值。,rt是当前位置,rig和lef是当前所在位置对应的区间端点,查询的参数差不多同理。
    void update(int lef,int rig,int rt,int a,int b,ll c){
        if(a<=lef&&b>=rig){//到需要修改的最底层才进行修改,然后返回修改路径上节点的值(它的父节点,父节点的父节点)。
            date[rt]+=(rig-lef+1)*c;
            tag[rt]+=c;//合并lazytag和区间加
            return;
        }
        push_down(rt,lef,rig);//向下传递lazytag,目的是在达到需要修改的最底层前把路劲上的值依照lazytag全都修改
        int mid=(lef+rig)>>1;
        if(a<=mid) update(lef,mid,lson,a,b,c);
        if(b>mid) update(mid+1,rig,rson,a,b,c);
        push_up(rt);//rt位置的值修改依赖于左右孩子的值的修改 
    }
    ll query(int lef,int rig,int rt,int a,int b){
        if(a<=lef&&b>=rig) return date[rt];
        push_down(rt,lef,rig);//向下传递lazytag,目的是在达到需要修改的最底层前把路劲上的值依照lazytag全都修改
        long long ans=0;
        int mid=(lef+rig)>>1;
        if(a<=mid) ans+=query(lef,mid,lson,a,b);
        if(b>mid) ans+=query(mid+1,rig,rson,a,b);
        return ans;
    }
}tree;

int main(){
    int n,m;
    cin>>n>>m;
    tree.init(1,n,1);
    int op;
    int x,y;
    ll tmp;
    while(m--){
        cin>>op;
        if(op==1){
            cin>>x>>y>>tmp;
            tree.update(1,n,1,x,y,tmp);//更新操作
        }
        else if(op==2){
            cin>>x>>y;
            cout<<tree.query(1,n,1,x,y)<<'\n';//查询操作
        }
    }
    return 0;
}

然后是区间加+区间乘法,有点恶心。
大体思路是把用两个lazy_tag维护乘法和加法。
对应的洛谷的题 https://www.luogu.com.cn/problem/P3373?contestId=43385
代码

#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
typedef long long ll;
typedef unsigned long long ull;
const ll MAXN=1e18;
const int MOD=1e6;

int p;

struct SegmentTree{
    ll date[400050];
    ll tag_add[400050];
    ll tag_time[400050];
    #define lson rt<<1
    #define rson rt<<1|1 
    void push_up(int rt){
        date[rt]=(date[lson]+date[rson])%p;
    }
    //密密麻麻地,,,
    //核心代码,核心代码,核心代码。
    //父节点和两儿子节点的lazytag相遇,把加法拿出来,乘法就直接乘。大概是要有num=num*time+add的感觉。其它都大同小异不多赘述。
    void push_down(int rt,int lef,int rig){
        if(tag_add[rt]==0&&tag_add[rt]==1) return;
        int mid=(lef+rig)>>1;
        tag_add[lson]=tag_add[lson]*tag_time[rt]%p+tag_add[rt]%p;
        tag_time[lson]=tag_time[lson]*tag_time[rt]%p;
        tag_add[rson]=tag_add[rson]*tag_time[rt]%p+tag_add[rt]%p;
        tag_time[rson]=tag_time[rson]*tag_time[rt]%p;
        date[lson]=date[lson]*tag_time[rt]%p+(mid-lef+1)*tag_add[rt]%p;
        date[rson]=date[rson]*tag_time[rt]%p+(rig-mid)*tag_add[rt]%p;
        tag_add[rt]=0;tag_time[rt]=1;
    }
    void init(int lef,int rig,int rt){
        tag_time[rt]=1;
        if(lef==rig) {cin>>date[rt];return;}
        int mid=(lef+rig)>>1;
        init(lef,mid,lson);
        init(mid+1,rig,rson);
        push_up(rt);
    }
    void update_add(int lef,int rig,int rt,int a,int b,ll c){
        if(a<=lef&&b>=rig){
            date[rt]=(date[rt]+(rig-lef+1)*c%p)%p;
            tag_add[rt]=(tag_add[rt]+c)%p;////和push_down差不多的道理
            return;
        }
        push_down(rt,lef,rig);
        int mid=(lef+rig)>>1;
        if(a<=mid) update_add(lef,mid,lson,a,b,c);
        if(b>mid) update_add(mid+1,rig,rson,a,b,c);
        push_up(rt); 
    }
    void update_time(int lef,int rig,int rt,int a,int b,ll c){
        if(a<=lef&&b>=rig){
            date[rt]=date[rt]*c%p;
            tag_add[rt]=tag_add[rt]*c%p;//和push_down差不多的道理
            tag_time[rt]=tag_time[rt]*c%p;
            return;
        }
        push_down(rt,lef,rig);
        int mid=(lef+rig)>>1;
        if(a<=mid) update_time(lef,mid,lson,a,b,c);
        if(b>mid) update_time(mid+1,rig,rson,a,b,c);
        push_up(rt); 
    }
    ll query(int lef,int rig,int rt,int a,int b){
        if(a<=lef&&b>=rig) return date[rt]%p;
        push_down(rt,lef,rig);
        long long ans=0;
        int mid=(lef+rig)>>1;
        if(a<=mid) ans+=query(lef,mid,lson,a,b);
        if(b>mid) ans+=query(mid+1,rig,rson,a,b);
        return ans%p;
    }
}tree;

int main(){
    int n,m;
    cin>>n>>m>>p;
    tree.init(1,n,1);
    int op;
    int x,y;
    ll tmp;
    while(m--){
        cin>>op;
        if(op==1){
            cin>>x>>y>>tmp;
            tree.update_time(1,n,1,x,y,tmp%p);
        }
        else if(op==2){
            cin>>x>>y>>tmp;
            tree.update_add(1,n,1,x,y,tmp%p);
        }
        else{
            cin>>x>>y;
            cout<<tree.query(1,n,1,x,y)<<'\n';
        }
    }
    return 0;
}

emmmm...常数就挺大的。

posted @ 2021-04-18 23:07  七铭的魔法师  阅读(55)  评论(0编辑  收藏  举报