Chapter 3. 数据结构 线段树

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Chapter 3. 数据结构 线段树相关的知识,希望对你有一定的参考价值。

Chapter 3. 数据结构 线段树

 

Sylvia‘s I.单点修改,区间查询.

  模板:   

//单点修改 区间求和 
//1操作 单点修改
//2操作 区间求和
#include<cstdio> #include<iostream> using namespace std; #define MAXN 500005 int sum[MAXN<<2]; int n,m; void PushUp(int rt){//求和 sum[rt]=sum[rt<<1]+sum[rt<<1|1]; } void build(int l,int r,int rt){//建树 if (l==r){ scanf("%d",&sum[rt]); return; } int m=l+r>>1; build(l,m,rt<<1); build(m+1,r,rt<<1|1); PushUp(rt); } void Update(int P,int Add,int l,int r,int rt){//单点修改 if (l==r){ sum[rt]+=Add; return; } int m=l+r>>1; if (P<=m) Update(P,Add,l,m,rt<<1); else Update(P,Add,m+1,r,rt<<1|1); PushUp(rt); } int Query(int L,int R,int l,int r,int rt){//区间查询 if (L<=l&&r<=R) { return sum[rt]; } int m=l+r>>1; int ret=0; if (L<=m) ret+=Query(L,R,l,m,rt<<1); if (m<R) ret+=Query(L,R,m+1,r,rt<<1|1); return ret; } int step,x,k; int main (){ scanf("%d%d",&n,&m); build(1,n,1); for(int i=1;i<=m;i++){ scanf("%d%d%d",&step,&x,&k); if (step==1) Update(x,k,1,n,1); if (step==2) printf("%d\\n",Query(x,k,1,n,1)); } return 0; }

Sylvia‘s II. 区间修改,区间查询.

模板:参考自:bogo的线段树模板

//1操作 乘法操作
//2操作 加法操作
//3操作 询问区间和
//最后的结果对P取模
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAX 100003
#define LL long long 
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

struct Segment_Tree{
    LL sum[MAX<<2];//数组开四倍
    LL lazy1[MAX<<2],lazy2[MAX<<2];//分别为乘法标记和加法标记
    LL P;//取模
    
    void PushUp(LL rt){//更新,对左右儿子求和
      sum[rt]=sum[rt<<1]+sum[rt<<1|1];
      sum[rt]%=P;//别忘记取模
    }
    
    void build(LL l,LL r,LL rt){//建树
        lazy1[rt]=1;//乘法标记记为1
        lazy2[rt]=0;//加法标记记为0
        if (l==r){
           scanf("%lld",&sum[rt]);
             return;
        }
        LL m=l+r>>1;
        build(lson);//左子树
        build(rson);//右子树
        PushUp(rt);//及时更新
        
    }
    
    void multiply (LL rt,LL x){//单乘 
        lazy2[rt]*=x;//(A[i]+lazy2[i])*lazy1[i]=A[i]*lazy1[i]+lazy2[i]*lazy1[i],所以加法标记要乘x 
        lazy2[rt]%=P;
        lazy1[rt]*=x;//乘法标记当然也要乘…… 
        lazy1[rt]%=P;
        sum[rt]*=x;//求和数组更要乘…… 
        sum[rt]%=P;
    }
    
    void Add (LL rt,LL x,LL len){//单加 ,len为区间长度 
        lazy2[rt]=(lazy2[rt]+x)%P;//加法标记加 
        sum[rt]=(sum[rt]+(x*len)%P)%P;//求和数组加 
    }
    
    void PushDown (LL l,LL r,LL rt){
        if (lazy1[rt]!=1){//如果有乘法标记 
            multiply(rt<<1,lazy1[rt]);//把标记下放到左儿子 
            multiply(rt<<1|1,lazy1[rt]);//把标记下放到右儿子 
            lazy1[rt]=1;//别忘记清除标记…… 
        }
        if (lazy2[rt]){//如果有加法标记…… 
            Add(rt<<1,lazy2[rt],(r+l>>1)-l+1);
            Add(rt<<1|1,lazy2[rt],r-(r+l>>1));
            lazy2[rt]=0;
        }
    } 
    
    void Addall(LL L,LL R,LL x,LL l,LL r,LL rt){//区间加 
        if (L<=l&&r<=R){//当前区间包含在需要查询的区间中 
            Add(rt,x,r-l+1);
            return;
        }
        LL m=l+r>>1;
        PushDown(l,r,rt);
        if (L<=m) Addall(L,R,x,lson);//如果需查询区间与左儿子有交集 
        if (m<R) Addall(L,R,x,rson);//如果需查询区间与右儿子有交集 
        PushUp(rt);//及时更新 
    }
    
    void Mullall(LL L,LL R,LL x,LL l,LL r,LL rt){// 区间乘 ,与上面类似…… 
        if (L<=l&&r<=R){
            multiply(rt,x);
            return;
        }
        LL m=l+r>>1;
        PushDown(l,r,rt);//不要忘记查询左右儿子之前先把标记下放,血的教训…… 
        if (L<=m) Mullall(L,R,x,lson);
        if (m<R) Mullall(L,R,x,rson);
        PushUp(rt);
    }
    
    LL Query(LL L,LL R,LL l,LL r,LL rt){//区间查询 
        if (L<=l&&r<=R){
            return sum[rt];
        }
        LL ret=0;
        LL m=l+r>>1;
        PushDown(l,r,rt);// 不要忘记下放标记 
        if (L<=m) ret=(ret+Query(L,R,lson))%P;
        if (m<R) ret=(ret+Query(L,R,rson))%P;
        return ret;    
    }
    
}Seg;
int main (){
    LL n,m,step,x,y,z;
    scanf("%lld%lld%lld",&n,&m,&Seg.P);
    Seg.build(1,n,1);//
    for (int i=1;i<=m;i++){
        scanf("%d",&step);
        switch(step){
            case 1:
                scanf("%lld%lld%lld",&x,&y,&z);
                Seg.Mullall(x,y,z,1,n,1);
                break;
            case 2:
                scanf("%lld%lld%lld",&x,&y,&z);
                Seg.Addall(x,y,z,1,n,1);
                break;
            case 3:
                scanf("%lld%lld",&x,&y);
                printf("%lld\\n",Seg.Query(x,y,1,n,1));
                break;
        }
    }
    return 0;
}

 

 

 

 


 

 

活着

余华

老人和牛渐渐远去,我听到老人粗哑的令人感动的嗓音从远处传来,

他的歌声在空旷的傍晚像风一样飘扬,

老人唱道:

少年去游荡,中年想掘藏,老年做和尚。

 

炊烟在农舍的屋顶袅袅升起,在霞光四射的空中分散后消隐了。

女人吆喝孩子的声音此起彼伏,一个男人挑着粪桶从我跟前走过,扁担吱呀吱呀一路响了过去。

慢慢地,田野趋向了宁静,四周出现了模糊,霞光逐渐退去。

 

我知道黄昏正在转瞬即逝,黑夜从天而降了。

我看到广阔的土地袒露着结实的胸膛,

那是召唤的姿态,

就像,

女人召唤着她们的儿女,土地召唤着黑夜来临。 

 


 

Sylvia

二零一七年五月十七日

 

以上是关于Chapter 3. 数据结构 线段树的主要内容,如果未能解决你的问题,请参考以下文章

数据结构--线段树

线段树详解

数据结构 ---[实现 线段树(SegmentTree) ]

Static Program Analysis - Chapter 2 代码的表征之抽象语法树

2018年6月1号(线段树)

数据结构——线段树