POJ 3468

题目:http://poj.org/problem?id=3468
解题思路:本题是一个裸的线段树,这里不过多赘述。
其实可以用树状数组来做,我们熟知的树状数组一般是单点更新,区间查询,但树状数组也可以进行区间更新
设要更新的区间是[l,r]
1、在l<=pos<=r时,前缀和的增量为val=(pos-l+1)*x
2、在pos>r时,前缀和增量为val=(r-l+1)*x
观察上述两式,我们可以看到公共增量-(l-1)*x,因此我们可以通过单点更新将该值加在pos>=l的前缀和上。
对于(2)式,剩余部分为r*x,同理,我们可以通过单点更新将该值加到pos>=r的前缀和上。
那么现在只剩下一部分,即对于l<=pos<=r中每一个pos,应该加上一个大小为pos*x的增量,但pos是变化的,所以我们其实可以在查询的时候为每个pos增加这个增量。
现在看来所有的处理已经结束了。其实不然,单点更新影响的是某点之后所有的前缀和,所以在更新pos*x的时候,pos>j的区间已经被破坏掉了。
现在我们可以考虑另一种做法,将该增量存储下来,作为lazytag,每次查询的时候再进行计算。
那处理的时候需要两个操作:
1、lazy[l]+=x;//对pos>=l的所有节点加x
2、lazy[r+1]-=x;//对pos>r的所有节点减x
每次查询的时候,对于区间[L,R],其求解公式为getSum(R)+getLazy(R)*R-getSum(L-1)-getLazy(L-1)(L-1)
其实这个式子很好理解,若是R在更新的区间内,L不在更新的区间内,那么结果就是R*x,此时getLazy(L-1)=0;若是R在,L也在,那么答案就是他们相差的数目cnt*x;若是R不在,L在,那R∈[r,无穷],进行计算的时候这部分不需要考虑增量的情况,但要减去L的那部分。
以上就是树状数组区间更新

下面附AC代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#define N 100005
#define ll long long
using namespace std;
ll lazy[N],data[N];

int lowbit(int x){
    return x&(-x);
}
void update(int pos,ll val){
    while(pos<N){
        data[pos]+=val;
        pos+=lowbit(pos);
    }
}
ll getSum(int pos){
    ll sum=0;
    while (pos>0){
        sum+=data[pos];
        pos-=lowbit(pos);
    }
    return sum;
}
void updateLazy(int pos,ll val){
    while (pos<N){
        lazy[pos]+=val;
        pos+=lowbit(pos);
    }
}
ll getLazy(int pos){
    ll sum=0;
    while(pos>0){
        sum+=lazy[pos];
        pos-=lowbit(pos);
    }
    return sum;
}
void init(){
    memset(data,0, sizeof(data));
    memset(lazy,0, sizeof(lazy));
}
int main(){
    int n,q;
    while(scanf("%d%d",&n,&q)!=EOF){
        init();
        ll x;
        for(int i=0;i<n;i++){
            scanf("%lld",&x);
            update(i+1,x);
        }
        for(int i=0;i<q;i++){
            char ch;
            int l,r;
            ll x;
            scanf(" %c",&ch);
            if(ch=='Q'){
                scanf("%d%d",&l,&r);
                cout<<getSum(r)<<' '<<getLazy(r)<<' '<<getSum(l-1)<<' '<<getLazy(l-1)<<endl;
                printf("%lld\n",getSum(r)+getLazy(r)*r-getSum(l-1)-getLazy(l-1)*(l-1));
            }else{
                scanf("%d%d%lld",&l,&r,&x);
                update(l,-(l-1)*x);
                updateLazy(l,x);
                update(r+1,r*x);
                updateLazy(r+1,-x);
            }
        }
    }
    return 0;
}

下面附参考链接:https://www.cnblogs.com/detrol/p/7586083.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值