Gym - 102220 - problem H. Skyscraper (差分数组 + 线段树)

链接:H. Skyscraper

题意:
有 n 座大楼排成一排,第 i 座大楼的预期高度为 ai,有m次操作:
1.把区间[l , r]内的大楼的预期高度增加 k.
2. 给定[l , r],查询仅对[l , r]区间内的大楼从0开始施工,到完成大楼修建至少需要多少个阶段。(每个阶段可以选择一个区间使得这个区间的大楼高度加1)

思路:

  1. 如果不考虑修改,假设a的差分数组为数组b, 那么完成[l , r] 区间内的大楼修建需要 a[l] + b[l + 1 , r]中大于 0 的值之和
  2. 对于修改,其实我们只需要单点修改就行,每次区间修改等价于 b[l] + k , b[r + 1] - k。求b数组中大于0的值可以再维护一个新的数组,把b中小于0的值变成0.求a的值,我们可以维护差分数组b的前缀和。

代码:

#include <iostream>
#include <cstdio>
#include <queue>
#include <math.h>
#include <map>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 7;
typedef long long ll;
const int mod=1e9 + 7;
int T , q , n;
int a[maxn];
ll b[maxn];
ll sum[3][maxn];
void pushup(int rt){
    sum[1][rt] = sum[1][rt << 1] + sum[1][rt << 1 | 1];
    sum[2][rt] = sum[2][rt << 1] + sum[2][rt << 1 | 1];
}
void update(int id , int pos,int val ,int l,int r,int rt){
      if(l == r){
         if(id == 1) sum[1][rt] = val;
         if(id == 2) sum[2][rt] = max(val , 0);
         return ;
      }
      int mid = (l + r) / 2;
      if(pos <= mid) update(id , pos ,val ,  l , mid , rt << 1);
      if(pos > mid) update(id ,pos ,val,  mid + 1 , r , rt << 1 | 1);
      pushup(rt);
}
ll query(int id , int L , int R,int l , int r,int rt){
    if(L <= l && R >= r){
        return sum[id][rt];
    }
    int mid = (l + r) / 2;
    ll ans = 0;
    if(L <= mid) ans += query(id , L , R , l , mid , rt << 1);
    if(R > mid) ans += query(id , L , R , mid + 1 , r , rt << 1 | 1);
    return ans;
}
int main(){
   scanf("%d",&T);
   while(T--){
       scanf("%d%d",&n,&q);
       memset(sum , 0 , sizeof(sum));
       for(int i = 1; i <= n; i ++){
           scanf("%d",&a[i]);
           b[i] = a[i] - a[i - 1];
           update(1 , i , b[i] , 1 , n , 1);
           update(2 , i , b[i] , 1 , n , 1);
       }
       int  op , l , r , k;
       while(q--){
            scanf("%d",&op);
            if(op == 1){
                scanf("%d%d%d",&l,&r,&k);
                b[l] += k;
                b[r + 1] -= k;
                update(1 , l , b[l], 1 , n , 1);
                update(2 , l , b[l], 1 , n , 1);
                if(r + 1 <= n){
                    update(1 , r + 1 , b[r + 1] , 1 , n , 1);
                    update(2 , r + 1 , b[r + 1] , 1 , n , 1);
                }
            }
            else{
                scanf("%d%d",&l,&r);
                ll ans = query(1 , 1 , l , 1 , n , 1);
                if(l + 1 <= r) ans += query(2 , l + 1 , r , 1 , n , 1);
                printf ("%lld\n",ans);
            }
       }
   }
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值