题意:
给出一个长为N的序列,需要支持两种操作:
1、求出在序列中某个位置加上x后,使得
2、将区间 al,al+1,al+2……ar a l , a l + 1 , a l + 2 … … a r 均加上x
分析:
这道题是用最优性来满足正确性的典型。
首先,我们考虑根据趋势,分为三种情况:
1.ai−1≤ai≤ai+1
1.
a
i
−
1
≤
a
i
≤
a
i
+
1
或
1.ai+1≤ai≤ai−1
1.
a
i
+
1
≤
a
i
≤
a
i
−
1
这种情况情况下,
如果
max(ai−1,ai+1)−ai≥x
m
a
x
(
a
i
−
1
,
a
i
+
1
)
−
a
i
≥
x
,那么在这个位置加x是不会对答案有影响的。
如果不然,那么答案就会加上:
x+x−2×(max(ai−1,ai+1)−ai)
x
+
x
−
2
×
(
m
a
x
(
a
i
−
1
,
a
i
+
1
)
−
a
i
)
在这种情况下,答案一定会得到正影响。
2.ai−1≥ai≤ai+1
2.
a
i
−
1
≥
a
i
≤
a
i
+
1
在这种情况下,如果要在这个点加x,是有可能为负数影响的,我们分析一下:
(我们假定ai−1≤ai+1)
(
我
们
假
定
a
i
−
1
≤
a
i
+
1
)
如果
ai+x≤ai−1,ai+1
a
i
+
x
≤
a
i
−
1
,
a
i
+
1
那么在这里加x,影响是
−2×x
−
2
×
x
,为负
如果
ai−1≤ai+x≤ai+1
a
i
−
1
≤
a
i
+
x
≤
a
i
+
1
在这里加x,影响是:
x−(x+ai−ai+1−(ai+1−ai))=2×(ai−ai+1)
x
−
(
x
+
a
i
−
a
i
+
1
−
(
a
i
+
1
−
a
i
)
)
=
2
×
(
a
i
−
a
i
+
1
)
也为负。
如果
ai+x≥ai−1,ai+1
a
i
+
x
≥
a
i
−
1
,
a
i
+
1
那么在这里加x,影响是
2×x−2×(ai+1−ai)−2×(ai−1−ai)
2
×
x
−
2
×
(
a
i
+
1
−
a
i
)
−
2
×
(
a
i
−
1
−
a
i
)
正负均可能。
3.ai−1,ai+1≤ai
3.
a
i
−
1
,
a
i
+
1
≤
a
i
在这种情况下,在这个点加x,那么影响为
2×x
2
×
x
,始终为正且最大。
仔细观察我们可能为正的三个式子:
1. 2×x−2×(max(ai−1,ai+1)−ai)
1.
2
×
x
−
2
×
(
m
a
x
(
a
i
−
1
,
a
i
+
1
)
−
a
i
)
2. 2×x−2×(ai+1−ai)−2×(ai−1−ai)
2.
2
×
x
−
2
×
(
a
i
+
1
−
a
i
)
−
2
×
(
a
i
−
1
−
a
i
)
3. 2×x
3.
2
×
x
很容易总结出,这三个式子都可以表示为:
2×x−max(0,ai−1−ai)−max(0,ai+1−ai)
2
×
x
−
m
a
x
(
0
,
a
i
−
1
−
a
i
)
−
m
a
x
(
0
,
a
i
+
1
−
a
i
)
这样一来,我们可以通过维护
max(0,ai−1−ai)+max(0,ai+1−ai)
m
a
x
(
0
,
a
i
−
1
−
a
i
)
+
m
a
x
(
0
,
a
i
+
1
−
a
i
)
的最小值,来得到对答案的影响。
有的小朋友就会问了,那么影响为负的情况怎么办呢?那两种式子不能表示为这种形式啊?
其实很容易发现,2类情况不可能充满一个大于1的区间,即:一个大于1的区间不可能全为波谷。
又因为另外两种情况答案均不为负,所以我们可以保证在大于1的区间内,一定不会造成负影响。(这里就是我个人认为这道题最巧妙的一点,虽然其本身很显然,但这种用最优性来保证正确性的思考方式很难得一见)
那么我们只需要对区间大小分类讨论:若区间大小为1,就只能在那个点加x,手动得到答案。
如区间大小不为1,就可以按照我们维护的值来求答案。
差点忘了还有修改操作:
很容易发现,其实如果在一个区间内均加上一个值,那么区间内部的差是不会影响的,因此我们只需要考虑两个端点即可,说白了这里的修改其实只是单点修改。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define SF scanf
#define PF printf
#define MAXN 100010
#define INF 1e18
using namespace std;
long long tree[MAXN*4],ans;
int n,m;
long long a[MAXN];
long long absx(long long x){
return x>0?x:-x;
}
void build(int id,int l,int r){
if(l==r){
ans+=absx(a[l]);
if(l!=1)
tree[id]=max(0ll,-a[l-1])+max(0ll,a[l]);
return ;
}
int mid=(l+r)>>1;
build(id<<1,l,mid);
build(id<<1|1,mid+1,r);
tree[id]=min(tree[id<<1],tree[id<<1|1]);
//PF("[%d %d %lld]",l,r,tree[id]);
}
void add(int id,int l,int r,int pos,long long x){
if(l==r){
ans-=absx(a[l]);
a[l]+=x;
ans+=absx(a[l]);
//PF("[%lld]",ans);
if(pos>1)
tree[id]=max(0ll,-a[l-1])+max(0ll,a[l]);
if(x!=0&&pos<n-1)
add(1,1,n-1,pos+1,0);
return ;
}
int mid=(l+r)>>1;
if(pos<=mid)
add(id<<1,l,mid,pos,x);
else
add(id<<1|1,mid+1,r,pos,x);
tree[id]=min(tree[id<<1],tree[id<<1|1]);
}
long long query(int id,int l,int r,int l1,int r1){
if(l>=l1&&r<=r1)
return tree[id];
int mid=(l+r)>>1;
long long res=INF;
if(l1<=mid)
res=min(res,query(id<<1,l,mid,l1,r1));
if(r1>mid)
res=min(res,query(id<<1|1,mid+1,r,l1,r1));
return res;
}
int main(){
int tag,l,r;
long long x;
SF("%d",&n);
for(int i=1;i<=n;i++)
SF("%d",&a[i]);
for(int i=1;i<n;i++)
a[i]=a[i+1]-a[i];
build(1,1,n-1);
SF("%d",&m);
for(int i=1;i<=m;i++){
SF("%d%d%d%lld",&tag,&l,&r,&x);
if(tag==1){
if(l==r){
if(l==1)
PF("%lld\n",ans-absx(a[1])+absx(a[1]-x));
else if(l==n)
PF("%lld\n",ans-absx(a[n-1])+absx(a[n-1]+x));
else
PF("%lld\n",ans-absx(a[l-1])+absx(a[l-1]+x)-absx(a[l])+absx(a[l]-x));
}
else{
long long res=max(0ll,2*x-2*query(1,1,n-1,max(2,l),r));
//PF("[%lld]",2*x-2*query(1,1,n-1,max(2,l),r));
if(l==1)
res=max(res,-absx(a[1])+absx(a[1]-x));
if(r==n)
res=max(res,-absx(a[n-1])+absx(a[n-1]+x));
PF("%lld\n",ans+res);
}
}
else{
if(l!=1)
add(1,1,n-1,l-1,x);
if(r!=n)
add(1,1,n-1,r,-x);
}
//PF("[%lld]",ans);
}
}