2021-07-31

如题,已知一个数列,你需要进行下面两种操作:

将某区间每一个数加上 kk。
求出某区间每一个数的和。
输入格式
第一行包含两个整数 n, mn,m,分别表示该数列数字的个数和操作的总个数。

第二行包含 nn 个用空格分隔的整数,其中第 ii 个数字表示数列第 ii 项的初始值。

接下来 mm 行每行包含 33 或 44 个整数,表示一个操作,具体如下:

1 x y k:将区间 [x, y] 内每个数加上 k。
2 x y:输出区间 [x, y]内每个数的和。

输出格式
输出包含若干行整数,即为所有操作 2 的结果。

输入
5 5
1 5 4 2 3
2 2 4
1 2 3 2
2 3 4
1 1 5 1
2 1 4
输出
11
8
20
在这里插入图片描述

#include<iostream>
#include<cstdio>
#define MAXN 1000001
#define ll long long
using namespace std;
unsigned ll n,m,a[MAXN],ans[MAXN<<2],tag[MAXN<<2];

ll ls(ll x){//x=x*2 左节点
    return x<<1;
}

ll rs(ll x){//x=x*2+1 右节点
    return x<<1|1;
}

void scan(){//输入数据
    cin>>n>>m;
    for(ll i=1;i<=n;i++)
    scanf("%lld",&a[i]);
}
void push_up(ll p){//更新节点信息,这里是求和
    ans[p]=ans[ls(p)]+ans[rs(p)];
}
//build函数建立线段树
void build(ll p,ll l,ll r){//l,r]表示当前节点区间,p表示当前节点的实际存储位置
    tag[p]=0;
    if(l==r){ans[p]=a[l];return ;}
    ll mid=(l+r)>>1;
    //左右递归
    build(ls(p),l,mid);
    build(rs(p),mid+1,r);
    //更新信息
    push_up(p);
} 

void f(ll p,ll l,ll r,ll k){
    tag[p]=tag[p]+k;
    ans[p]=ans[p]+k*(r-l+1);
}0
void push_down(ll p,ll l,ll r){
    ll mid=(l+r)>>1;
    f(ls(p),l,mid,tag[p]);
    f(rs(p),mid+1,r,tag[p]);
    tag[p]=0;
}
// 区间更新,[nl,nr]为更新范围,[l,r]为线段树范围,k为更新值
void update(ll nl,ll nr,ll l,ll r,ll p,ll k){
    if(nl<=l&&r<=nr)
    {
        ans[p]+=k*(r-l+1);
        tag[p]+=k;
        return ;
    }
    push_down(p,l,r);
    ll mid=(l+r)>>1;
    if(nl<=mid)update(nl,nr,l,mid,ls(p),k);
    if(nr>mid) update(nl,nr,mid+1,r,rs(p),k);
    push_up(p);
}
//区间查询(求和)
ll query(ll q_x,ll q_y,ll l,ll r,ll p){//[q_x,q_y]表示操作区间,[l,r]表示当前区间,p当前节点编号
    ll res=0;
    if(q_x<=l&&r<=q_y) return ans[p] ; //在区间内直接返回
    ll mid=(l+r)>>1;
    push_down(p,l,r);
    if(q_x<=mid)res+=query(q_x,q_y,l,mid,ls(p));//左子区间与[q_x,q_y]有重叠,递归
    if(q_y>mid) res+=query(q_x,q_y,mid+1,r,rs(p));//右子区间与[q_x,q_y]有重叠,递归
    return res;
}

int main(){
    ll a1,b,c,d,e,f;
    scan();//
    build(1,1,n);
    while(m--)
    {
        scanf("%lld",&a1);
        switch(a1)
        {
            case 1:{
                scanf("%lld%lld%lld",&b,&c,&d);
                update(b,c,1,n,1,d);
                break;
            }
            case 2:{
                scanf("%lld%lld",&e,&f);
                printf("%lld\n",query(e,f,1,n,1));
                break;
            }
        }
    }
    return 0;
}```

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值