题目
Input
输入文件名为lipschitz.in。
第一行一个整数n。
接下来一行n个整数,描述序列A。
第三行一个数q 。
接下来q行,每行三个整数。其中第一个整数type表示操作的类型。 type=0对应修改操作, type=1对应查询操作。
Output
输出文件名为lipschitz.out。
对于每个查询,给出f(A[l..r]) 。
Sample Input
6
90 50 78 0 96 20
6
0 1 35
1 1 4
0 1 67
0 4 11
0 3 96
1 3 5
Sample Output
78
85
Data Constraint
对于30%的数据,n,q<=500
对于60%的数据,n,q<=5000
对于100%的数据,n,q<=100000,0<=ai,val<=10^9
思路
比赛时是猜想应该只和相邻的有关系,不过没时间去证了。
解法
首先能证明出最优答案一定是选择两个相邻的数。
假设三个数a < b < c,a与b间有x个数,b与c之间有y个数。我们令选a,c比选a,b与b,c更优。
选a,b代价为
Vab=b−ax+1
,选b,c代价为
Vbc=c−by+1
选a,c代价为
Vac=c−ax+y+2
令
Vab<Vac,Vbc<Vac
则
(b−a)(x+y+2)<(c−a)(x+1),(c−b)(x+y+2)<(c−a)(y+1)
两个式子一加,发现左边=右边,这就意味着两个式子至少有一项不符合。
所以只需要用一个数组存下相邻两个数的差的绝对值,每次修改是修改两个,维护最大值,用上一些数据结构即可。
时间O(n log n).
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cmath>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=1e5+5;
int tree[maxn*4],a[maxn],n,q,ans,b[maxn];
void maketree(int p,int l,int r)
{
if (l==r) tree[p]=b[l];
else {
int mid=(l+r)>>1;
maketree(p<<1,l,mid);
maketree(p<<1|1,mid+1,r);
tree[p]=max(tree[p<<1],tree[p<<1|1]);
}
}
void change(int p,int l,int r,int pos,int val)
{
if (l==r) tree[p]=val;
else {
int mid=(l+r)>>1;
if (pos<=mid) change(p<<1,l,mid,pos,val);
else change(p<<1|1,mid+1,r,pos,val);
tree[p]=max(tree[p<<1],tree[p<<1|1]);
}
}
int getans(int p,int l,int r,int a,int b)
{
if (l==a&&r==b) ans=max(ans,tree[p]);
else {
int mid=(l+r)>>1;
if (b<=mid) getans(p<<1,l,mid,a,b);
else if (a>mid) getans(p<<1|1,mid+1,r,a,b);
else {
getans(p<<1,l,mid,a,mid);
getans(p<<1|1,mid+1,r,mid+1,b);
}
}
}
int main()
{
freopen("lipschitz.in","r",stdin);
freopen("lipschitz.out","w",stdout);
scanf("%d",&n);
fo(i,1,n) scanf("%d",&a[i]),b[i-1]=abs(a[i]-a[i-1]);
maketree(1,1,n-1);
scanf("%d",&q);
fo(i,1,q){
int t,x,y;
scanf("%d%d%d",&t,&x,&y);
if (!t) {
a[x]=y;
b[x-1]=abs(a[x]-a[x-1]);
b[x]=abs(a[x]-a[x+1]);
change(1,1,n-1,x-1,b[x-1]);
change(1,1,n-1,x,b[x]);
}
else {
ans=0;
if (x!=y) getans(1,1,n-1,x,y-1);
printf("%d\n",ans);
}
}
}