线段树_poj_3468_A Simple Problem with Integers

题目链接

线段树题目。

包含基本操作:区间更新,区间求和。
区间更新:由于这里更新是对区间进行的,所以如果每次更新都更新到区间里的每个值上去的话那线段树的优势就没有了,所以就加一个懒惰标记,进行更新时就像之前普通的线段树区间查询那样, 从第一个点一直往下,如果整个区间被包含就标记为更新并记录下要跟新的值,如果不被全部包含则往下一层分。 这很容易理解。
但如果更新的时候有一段区间之前被部分更新,如果这时候还在这上面更新的话那就乱套了,因为并不需要全部被更新,所以这时候就把先前标记的往下推一层,并把本层的标记取消,然后再进行新的更新,然后以此类推,操作都是一样的。

区间查询:这里区间查询与普通的线段树区间查询也不一样,因为前面更新的不一样,所以查询也有一定的区别,其实思想还是和上面的一样理解了更新,查询自然不是问题。查询的时候也像那样,如果访问的区间正好是你要的区间,那么直接return就好了,如果不是并且有标记的话,就把标记与值往下更新,本层的标记取消,以此类推。。

懒惰标记的巧妙实现让区间更新也能在log(N)内实现了,主要思想是先不更新到每个具体的节点,要用的时候在往下更新,懒人思想。。

代码君

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
int t[100010];
ll a[400010];
int lazy[400010];
ll val[400010];

void build(int l,int r,int k){
    if(l==r){
        a[k]=t[l];
        return;
    }
    int m=(l+r)>>1;
    build(l,m,k<<1);
    build(m+1,r,k<<1|1);
    a[k]=a[k<<1]+a[k<<1|1];
}

void pushdown(int k,int l,int r){
    if(lazy[k]){
        int tmp=(r-l+1)/2;
        lazy[k<<1|1]=1;
        lazy[k<<1]=1;
        a[k<<1|1]+=tmp*val[k];
        a[k<<1]+=(r-l+1-tmp)*val[k];
        val[k<<1]+=val[k];
        val[k<<1|1]+=val[k];
        lazy[k]=0;
        val[k]=0;
    }
}

void pushup(int k){
    if(k==0)return;
    a[k]=a[k<<1]+a[k<<1|1];
    pushup(k>>1);
}

void update(int l,int r,int v,int k,int ragl,int ragr){
    if(l==ragl && r==ragr){
        a[k]+=(r-l+1)*v;
        val[k]+=v;
        lazy[k]=1;
        return ;
    }
    pushdown(k,ragl,ragr);
    int mid=(ragl+ragr)>>1;
    if(r<=mid) update(l,r,v,k<<1,ragl,mid);
    else if(l>mid) update(l,r,v,k<<1|1,mid+1,ragr);
    else {
        update(l,mid,v,k<<1,ragl,mid);
        update(mid+1,r,v,k<<1|1,mid+1,ragr);
    }
    pushup(k);
}

ll query(int l,int r,int k,int ragl,int ragr){
    if(l==ragl && r==ragr){
        return a[k];
    }
    pushdown(k,ragl,ragr);
    int mid=(ragl+ragr)>>1;
    if(r<=mid) return query(l,r,k<<1,ragl,mid);
    else if(l>mid) return query(l,r,k<<1|1,mid+1,ragr);
    else return query(l,mid,k<<1,ragl,mid)+query(mid+1,r,k<<1|1,mid+1,ragr);
}

int main(){
    int N,Q;
    while(cin>>N>>Q){
        memset(lazy,0,sizeof(lazy));
        memset(val,0,sizeof(val));
        for(int i=1;i<=N;i++)scanf("%d",&t[i]);
        build(1,N,1);
        int l,r,v;
        char op;
        for(int i=0;i<Q;i++){
            getchar();
            scanf("%c",&op);
            if(op=='Q'){
                scanf("%d%d",&l,&r);
                printf("%lld\n",query(l,r,1,1,N));
            }else{
                scanf("%d%d%d",&l,&r,&v);
                update(l,r,v,1,1,N);
            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值