题目:https://vjudge.net/contest/425557#problem/D
题意:……
参考:参考链接
思路:
- 事实上是从前往后盖楼,在盖上一座楼
d
i
−
1
d_{i-1}
di−1时,有能力使当前楼盖高度
d
i
d_i
di
≤
d
i
−
1
\le d_{i-1}
≤di−1
因此我们盖当前楼需要的高度需要的花费为 m a x ( 0 , d i − d i − 1 ) max(0,d_i-d_{i-1}) max(0,di−di−1) - 用差分+树状数组维护,这里需要求出一段区间中大于0的数的和
- 由于每次所需的差分区间的起点不确定,起点的前面没有楼,起点应为实际高度,这里还需要一个树状数组维护实际高度
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
const int INF=0x3f3f3f3f;
const int N=1e5+5;
LL tr1[N], tr2[N], a[N], b[N];
int n,m;
int lowbit(int x){
return x&-x;
}
void add(int x, LL c, LL tr[]){
for(int i=x; i<=n; i+=lowbit(i)) tr[i]+=c;
}
LL sum(int x, LL tr[]){
LL res=0;
for(int i=x; i; i-=lowbit(i))
res+=tr[i];
return res;
}
int main(){
int T;
cin>>T;
while(T--){
memset(tr1, 0, sizeof tr1);
memset(tr2, 0, sizeof tr2);
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++){
scanf("%lld",&a[i]);
b[i]=a[i]-a[i-1];
add(i, b[i], tr1);
add(i, max(b[i], (LL)0), tr2);
}
while(m--){
int op, l, r, k;
scanf("%d%d%d",&op,&l,&r);
if(op==1){
scanf("%d",&k);
add(l, k, tr1), add(r+1, -k, tr1);
if(b[l]>0) add(l, k, tr2);
else if(b[l]+k>0) add(l ,b[l]+k, tr2);
if(b[r+1]>0){
if(b[r+1]-k>=0) add(r+1, -k, tr2);
else add(r+1, -b[r+1], tr2);
}
b[l]+=k, b[r+1]-=k;
}
else{
LL v1=sum(l, tr1)+sum(r, tr2)-sum(l, tr2); //起点已经有了,所以并不是sum(r)-sum(l-1)
printf("%lld\n",v1);
}
}
}
return 0;
}