题意:
有 n 座大楼排成一排,第 i 座大楼的预期高度为 ai,有m次操作:
1.把区间[l , r]内的大楼的预期高度增加 k.
2. 给定[l , r],查询仅对[l , r]区间内的大楼从0开始施工,到完成大楼修建至少需要多少个阶段。(每个阶段可以选择一个区间使得这个区间的大楼高度加1)
思路:
- 如果不考虑修改,假设a的差分数组为数组b, 那么完成[l , r] 区间内的大楼修建需要 a[l] + b[l + 1 , r]中大于 0 的值之和。
- 对于修改,其实我们只需要单点修改就行,每次区间修改等价于 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);
}
}
}
}