洛谷 P3372 【模板】线段树 1

题目链接:

https://www.luogu.org/problemnew/show/P3372

题意:

给你n个数,然后执行m个操作,操作分两种,对一个区间内的每个数都加上一个值,或者进行某个区间和的查询.

分析:

线段树裸题,注意lazy标记的使用;
对于lazy的个人理解,当你进行更新或者操作之时,你需要只要到你需要进行操作的区间就行,当你进行给一个区间进行加值的操作,如果没有lazy,你会递归到树的叶子节点。而使用lazy之后,你只需要到那一个或者两个包含你需要操作的区间的节点,将该节点的sum加上区间长度乘以这个你要加的值,这是更新sum,但是它的子节点,即那些小区间的sum并没有改变,于是就有了lazy,到了这一个或者两个需要更新的节点,然后并不继续向下,而是将这个需要加的值加到lazy之上,等到下一次我需要进行访问它的子节点的时候,在把它传下去;其实就是访问到刚好需要操作的区间,等待下一次操作访问到,在继续进行修改。

下面给出书上叙述:

       如果我们在一次修改指令中发现节点 p p p代表的区间[ p p p r r r, p p p l l l]被修改区间 [ l , r ] [l,r] [l,r]完全覆盖,并且逐一更新了子树 p p p中的所有节点,但是在之后的查询指令中却根本没有用到 [ l , r ] [l,r] [l,r]的子区间作为候选答案,那么更新 p p p的整颗子树就是徒劳的。
       换言之,我们在执行修改指令时,同样可以在 l < = l<= l<= p p p r r r < = <= <= p p p r r r < = r <=r <=r的情况下立即返回,只不过在回溯之前向节点p增加一个标记,标识 " " "该节点曾经被修改,但其子节点尚未被更新 " " "
       然后在后续的指令中,需要从节点 p p p向下递归,我们再检查 p p p是否具有标记。若有标记,就根据标记信息更新 p p p的两个子节点,同时为 p p p的两个子节点增加标记,然后清除标记。
       就是说,除了在修改指令中直接划分成的 O ( l o g N ) O(logN) O(logN)个节点之外,对任意节点的修改都延迟到 " " "在后续操作递归进入他的父节点时 " " "再执行。这样对每条查询或修改指令的时间复杂度都降低到了 O ( l o g N ) O(logN) O(logN)。这些标记即为 l a z y lazy lazy标记。
(有些题目会出现对多种对区间修改的操作,例如同时出现乘法、加法、减法,这样就不仅仅是一个 l a z y lazy lazy了)

代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;

const double inf=0x7f7f7f7f;
const int maxn=1e5+50;
const int N=2e4+50;
typedef long long ll;
typedef struct{
    int u,v,next,lca;
}Edge;
Edge e[2*maxn];

typedef struct B{
    int l,r;
    ll sum,lazy;
    void update(int value){
        sum+=(r-l+1)*value;
        lazy+=value;
    }
}Tree;
Tree tree[4*maxn];
int cnt,head[maxn];

void add(int u,int v){
    e[cnt].u=u;
    e[cnt].v=v;
    /*e[cnt].w=w;
    e[cnt].f=f;*/
    e[cnt].next=head[u];
    head[u]=cnt++;
    e[cnt].u=v;
    e[cnt].v=u;
/*  e[cnt].w=0;
    e[cnt].f=-f;*/
    e[cnt].next=head[v];
    head[v]=cnt++;
}

int read()
{
    int x = 0;
    int f = 1;
    char c = getchar();
    while (c<'0' || c>'9')
    {    
        if (c == '-')
            f = -1;
        c = getchar();
    }
    while (c >= '0'&&c <= '9')
    {
        x = x * 10 + c - '0';
        c = getchar();
    }
    return x*f;
}

int n,m,x,y,k,p;


void push_up(int x){
    tree[x].sum=tree[x<<1].sum+tree[x<<1|1].sum;
}
void push_down(int x){
    tree[x<<1].update(tree[x].lazy);
    tree[x<<1|1].update(tree[x].lazy);
    tree[x].lazy=0;
}
void build(int x,int l,int r){
    tree[x].l=l,tree[x].r=r,tree[x].sum=0;
    if(l==r){
        scanf("%lld",&tree[x].sum);
        return ;
    }
    int mid=(l+r)/2;
    build(x<<1,l,mid);
    build(x<<1|1,mid+1,r);
    push_up(x);
}

void update(int x,int l,int r,int k){
    int left=tree[x].l,right=tree[x].r;
    if(l<=left&&r>=right){
        tree[x].update(k);
        return ;
    }
    push_down(x);
    int mid=(left+right)/2;
    if(l<=mid)update(x<<1,l,r,k);
    if(r>mid)update(x<<1|1,l,r,k);
    push_up(x);
}

ll query(int x,int l,int r){
    int left=tree[x].l,right=tree[x].r;
    int mid=(left+right)/2;
    ll ans=0;
    if(l<=left&&r>=right){
        ans+=tree[x].sum;
    }
    else{
            push_down(x);
            if(l<=mid)ans+=query(x<<1,l,r);
            if(r>mid)ans+=query(x<<1|1,l,r);
            push_up(x);
    }
    return ans;
}
int main() {
    cin>>n>>m;
    build(1,1,n);
    int number;
    for(int i=0;i<m;i++){
        scanf("%d",&number);
        if(number==1){
                scanf("%d %d %d",&x,&y,&k);
                update(1,x,y,k);
                //cout<
        }
        else if(number==2){
            scanf("%d %d",&x,&y);
            printf("%lld\n",query(1,x,y));
        }
    }
  /*  cout<

}   

(仅供个人理解)

你可能感兴趣的:(数据结构--线段树)