seq 差分
Description
白云建立了n个商店,白兔打算按照编号1 . . . n的顺序访问这些商店。 商店i有一个
价格a i 表示交易商品所需的代价。
白兔在按顺序走时,每到达一个商店,可以花费代价购买一件商品,并放入自己手
中。也可以出售手上的商品,并获得利润。
白兔的力量有限,同一时刻只能携带一个商品。问它遍历完所有商店后能够获得的
利润最大是多少?
白兔的精力也有限,所以,在最大化利润的前提下,它想让交易次数尽可能地少。
当然,白云不想让白兔轻松获利,它有时会命令一段区间内的商店把价格同时加上
一个数。
solution
显然是找到许多个递增段,每一次该段的最大值减去最小值,我们可以线段树维护这个东西,但是有一个巧妙的差分思想:我们该原数组为差分数组后,问题就转化为求所有正数和,修改也变成了单点修改,第一问就做完了,第二问我们只需要特判一个 \(0\) 即可,我们维护正数区间的段,直接线段树合并时维护即可,我们发现如果0和一个正数接在一起,我们可以当做一段,这样就达到了第二问最小化购买的要求
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define RG register
#define il inline
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
#define ls (node<<1)
#define rs (node<<1|1)
using namespace std;
const int N=100005;
typedef long long ll;
int n,Q,a[N];
ll tot=0;
int tr[N<<2],fi[N<<2],suf[N<<2],sum[N<<2];
il void upd(int node){
tr[node]=tr[ls]+tr[rs]-(suf[ls]&sum[rs]);
suf[node]=suf[rs]|(fi[rs]&suf[ls]);
sum[node]=sum[ls]|(fi[ls]&sum[rs]);
fi[node]=fi[ls]&fi[rs];
}
il void ins(int l,int r,int node,int sa){
if(l==r){
suf[node]=sum[node]=tr[node]=a[sa]>0,fi[node]=a[sa]==0;
return ;
}
int mid=(l+r)>>1;
if(sa<=mid)ins(l,mid,ls,sa);
else ins(mid+1,r,rs,sa);
upd(node);
}
il void build(int l,int r,int node){
if(l==r){
suf[node]=sum[node]=tr[node]=a[l]>0,fi[node]=a[l]==0;
return ;
}
int mid=(l+r)>>1;
build(l,mid,ls);build(mid+1,r,rs);
upd(node);
}
void Clear(){
int lim=N*4-1;
for(RG int i=0;i<lim;i++)
tr[i]=fi[i]=sum[i]=suf[i]=0;
tot=0;
}
void work()
{
Clear();
scanf("%d%d",&n,&Q);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=n;i>=1;i--)a[i]-=a[i-1];
a[1]=0;
for(int i=2;i<=n;i++)tot+=Max(a[i],0);
build(1,n,1);
int flag,l,r,c;
while(Q--){
scanf("%d",&flag);
if(!flag){
scanf("%d%d%d",&l,&r,&c);
if(l>1){
tot-=Max(a[l],0);
a[l]+=c;
tot+=Max(a[l],0);
ins(1,n,1,l);
}
if(r<n){
tot-=Max(a[r+1],0);
a[r+1]-=c;
tot+=Max(a[r+1],0);
ins(1,n,1,r+1);
}
}
else printf("%lld %d\n",tot,tr[1]<<1);
}
}
int main(){
int T;cin>>T;
while(T--)work();
return 0;
}