题意
有一个长度为
n
n
的序列,每次询问区间
[l,r]
[
l
,
r
]
中任意两个数的差的绝对值的最小值。
n≤105,m≤3∗105
n
≤
10
5
,
m
≤
3
∗
10
5
分析
从小到大枚举右端点,用线段树维护每个点作为左端点时的答案。
考虑每次新加入一个点
a[i]
a
[
i
]
时要如何维护答案。
这里只考虑维护所有
a[j]≤a[j]
a
[
j
]
≤
a
[
j
]
且
j<i
j
<
i
时的答案,另一种情况可以把
a
a
数组取反后再做一遍。
我们可以先找到前面第一个不大于
a[i]
a
[
i
]
的数
a[j]
a
[
j
]
,然后用
a[i]−a[j]来更新
a
[
i
]
−
a
[
j
]
来
更
新
ans[1..j]
a
n
s
[
1..
j
]
。然后再找到
j
j
前面第一个大于且不大于
a[i]
a
[
i
]
的数
a[k]
a
[
k
]
,然后用
a[i]−a[k]
a
[
i
]
−
a
[
k
]
来更新
ans[1..k]
a
n
s
[
1..
k
]
。如此类推。
这样做是
O(n2logn)
O
(
n
2
l
o
g
n
)
的,但这里有一个优化,就是
a[k]
a
[
k
]
必须要满足
a[k]−a[j]>a[i]−a[k]
a
[
k
]
−
a
[
j
]
>
a
[
i
]
−
a
[
k
]
。
为什么呢?因为如果不满足的话,那么
ans[1..k]
a
n
s
[
1..
k
]
一定不会取
a[i]−a[k]
a
[
i
]
−
a
[
k
]
作为答案,而是取
a[k]−a[j]
a
[
k
]
−
a
[
j
]
作为答案。
这样的话每次取的点到
a[i]
a
[
i
]
的距离就会至少减少一半,就可以过了。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
const int N=100005;
const int inf=1000000000;
int n,m,ans[N*3],a[N],lef[N*3];
struct tree{int mn;}t[N*4];
std::vector<int> vec[N];
int read()
{
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
struct Segment_Tree
{
int rt[N],sz;
struct tree{int l,r,mx;}t[N*33];
void ins(int &d,int l,int r,int x,int y)
{
int p=d;d=++sz;t[d]=t[p];t[d].mx=std::max(t[d].mx,y);
if (l==r) return;
int mid=(l+r)/2;
if (x<=mid) ins(t[d].l,l,mid,x,y);
else ins(t[d].r,mid+1,r,x,y);
}
int query(int d,int l,int r,int x,int y)
{
if (!d) return 0;
if (x<=l&&r<=y) return t[d].mx;
int mid=(l+r)/2,ans=0;
if (x<=mid) ans=std::max(ans,query(t[d].l,l,mid,x,y));
if (y>mid) ans=std::max(ans,query(t[d].r,mid+1,r,x,y));
return ans;
}
}seg;
void build(int d,int l,int r)
{
t[d].mn=inf;
if (l==r) return;
int mid=(l+r)/2;
build(d*2,l,mid);build(d*2+1,mid+1,r);
}
void modify(int d,int l,int r,int x,int y,int z)
{
if (x<=l&&r<=y) {t[d].mn=std::min(t[d].mn,z);return;}
int mid=(l+r)/2;
if (x<=mid) modify(d*2,l,mid,x,y,z);
if (y>mid) modify(d*2+1,mid+1,r,x,y,z);
}
int find(int d,int l,int r,int x)
{
if (l==r) return t[d].mn;
int mid=(l+r)/2;
if (x<=mid) return std::min(t[d].mn,find(d*2,l,mid,x));
else return std::min(t[d].mn,find(d*2+1,mid+1,r,x));
}
void solve()
{
build(1,1,n);
for (int i=1;i<=n;i++)
{
int x=a[i],p=seg.query(seg.rt[i-1],0,inf,0,x);
while (p)
{
modify(1,1,n,1,p,x-a[p]);
p=seg.query(seg.rt[p-1],0,inf,(x+a[p])/2+1,x);
}
for (int j=0;j<vec[i].size();j++) ans[vec[i][j]]=std::min(ans[vec[i][j]],find(1,1,n,lef[vec[i][j]]));
}
}
int main()
{
n=read();
for (int i=1;i<=n;i++) a[i]=read();
for (int i=1;i<=n;i++) seg.rt[i]=seg.rt[i-1],seg.ins(seg.rt[i],0,inf,a[i],i);
m=read();
for (int i=1;i<=m;i++)
{
int l=read(),r=read();
vec[r].push_back(i);
lef[i]=l;ans[i]=inf;
}
solve();
for (int i=1;i<=n;i++) a[i]=inf-a[i];
seg.sz=0;
for (int i=1;i<=n;i++) seg.rt[i]=seg.rt[i-1],seg.ins(seg.rt[i],0,inf,a[i],i);
solve();
for (int i=1;i<=m;i++) printf("%d\n",ans[i]);
return 0;
}