poj3468 A Simple Problem with Integers 线段树/分块/树状数组

题目

给定长度为N(N<=100000)的数列A,然后输入Q(Q<=100000)行操作指令
第一类指令形如“C l r d”,表示把数列中第l~r个数都加d
第二类指令形如“Q l r”,表示询问数列中第l~r个数的和

题解

可以用分块/线段树/树状数组
注意相加后答案可能超出32位整数

分块
分块就是通过适当的划分,预处理一部分信息并保存下来,方便后面的查询。不一定像这题涉及数量的加减,也可以有其他的
分块的效率往往比不上树状数组和线段树,但分块更好理解,更好实现
分块一般是把数组分成若干个长度不超过√n的段,然后对这些段的数据进行处理
在这题里,用
add[i]表示区间i所有数所加的量,初始值为0
a[i]表示每个数,整段修改时只修改add[i],不修改a[i]
sum[i]表示区间i的和,整段修改时不修改,a[i]的变化计入
对于修改或查询的区间l~r(例:2~9)
第2个数处于区间1,第9个数处于区间3
要整段修改或查询的是1+1~3-1区间,即区间2,就把区间2的add[i]修改,或把区间2的add[i]*区间2的长度计入答案
要朴素修改或查询的是2~区间1末尾&区间3开头~9,即2~3和7~9,就分别对每一个数的a[i]修改,计入sum[i],查询就把a[i]计入答案

 1 2 3 | 4 5 6 | 7 8 9 | 10
  区间1| 区间2 | 区间3 |区间4

分块,“大段维护,局部朴素”

线段树
很适合这题啊

树状数组
不会

代码

分块

#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>

using namespace std;

const int N=100005;
long long add[N],sum[N],a[N];
int n,m,t,p[N],L[N],R[N];

int main(){
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
    t=sqrt(n);
    for (int i=1;i<=t;i++){
        L[i]=(i-1)*t+1;
        R[i]=i*t;
    }
    if (R[t]<n) L[++t]=R[t-1]+1,R[t]=n;
    for (int i=1;i<=t;i++)
        for (int j=L[i];j<=R[i];j++){
            p[j]=i;
            sum[i]+=a[j];
        }
    for (int i=1;i<=m;i++){
        char c[2];
        int l,r,x,y,k;
        long long ans=0;
        scanf("%s%d%d",&c,&l,&r);
        x=p[l];
        y=p[r];
        if (c[0]=='Q'){             
            if (x!=y){
                for (int j=x+1;j<=y-1;j++)
                    ans+=add[j]*(R[j]-L[j]+1)+sum[j];
                for (int j=l;j<=R[x];j++)
                    ans+=a[j];
                ans+=add[x]*(R[x]-l+1);
                for (int j=L[y];j<=r;j++)
                    ans+=a[j];
                ans+=add[y]*(r-L[y]+1);
            } else 
            {
                for (int j=l;j<=r;j++)
                    ans+=a[j];
                ans+=add[x]*(r-l+1);
            }
            printf("%lld\n",ans);
        }else{
            scanf("%d",&k);         
            if (x!=y){
                for (int j=x+1;j<=y-1;j++)
                    add[j]+=k;
                for (int j=l;j<=R[x];j++)
                    a[j]+=k;
                for (int j=L[y];j<=r;j++)
                    a[j]+=k;
                sum[x]+=(R[x]-l+1)*k;
                sum[y]+=(r-L[y]+1)*k;
            } else 
            {
                for (int j=l;j<=r;j++)
                    a[j]+=k;
                sum[x]+=(r-l+1)*k;
            }

        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值