題意:給定一個長度爲n的數組與m個查詢。第i個查詢要求輸出數組下標區間[li,ri]內的數之間的距離(差的絕對值)的最小值。
分析:離線處理查詢。將查詢區間按右端點從小到大排序,維護一個數據結構,使得將數組元素a1,a2,...,ai插入到數據結構之後能夠快速查詢以ai爲右端點的查詢區間的答案。考慮ai對查詢區間的貢獻,設d=|ai-aj|(j<i),則對於滿足l≤j,r=a[i]的查詢區間[l,r],有查詢區間[l,r]的答案≤d。因此可以用線段樹來維護答案,線段樹每個結點記錄左端點處於對應範圍內時(當右端點 爲ai時)的查詢區間的上確界。
若暴力處理ai對查詢區間的貢獻,則時間複雜度爲O(n),這顯然是不可接受的。不妨先設a1,a2,...,ai-1均大於等於ai(一般情況下處理2次即可),則在計算完元素對(ai,ai-1)對查詢區間的貢獻後,設下一個選取的元素對爲(ai,aj),則aj應滿足aj≤(ai+ai-1)/2。這是因爲:1.aj≥ai-1時元素對(ai,aj)不優於元素對(ai,ai-1)的貢獻;2.(ai+ai-1)/2≤aj≤ai-1時元素對(ai,aj)的貢獻不優於元素對(ai-1,aj)的貢獻。依次類推,計算ai對查詢區間的貢獻時間複雜度可以優化到O(lgn)。
下面討論程序的實現。我們需要一個數據結構,使得能夠快速求出數組下標在[1,j-1]範圍內大小在範圍[ai,(ai+aj)/2]內的下標最大的數。可以建一棵線段樹,線段樹的每個結點[l,r]存一個vector,vector裏將al,al+1,...,ar按從小到大排序,然後就可以O(lgn^2)地完成這件事(官方題解似乎有O(lgn)的做法)。
代碼:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
struct que
{
int l,r,i;
bool operator < (const que &o) const
{
return r<o.r;
}
}qu[maxn];
int n,m,a[maxn],ans[maxn];
vector<int> vec[maxn*4];
int minv[maxn*4],ql,qr,p,val;
void build(int o,int l,int r)
{
if (l==r) {vec[o].push_back(a[l]);return ;}
int mid=(l+r)/2;
build(2*o,l,mid);build(2*o+1,mid+1,r);
int i=0,j=0;
while (i<vec[2*o].size()&&j<vec[2*o+1].size())
{
if (vec[2*o][i]<=vec[2*o+1][j]) vec[o].push_back(vec[2*o][i]),i++;
else vec[o].push_back(vec[2*o+1][j]),j++;
}
while (i<vec[2*o].size()) vec[o].push_back(vec[2*o][i]),i++;
while (j<vec[2*o+1].size()) vec[o].push_back(vec[2*o+1][j]),j++;
}
int find(int o,int l,int r,int mine,int maxe)
{
if (ql<=l&&r<=qr)
{
vector<int>::iterator it=lower_bound(vec[o].begin(),vec[o].end(),mine);
if (it==vec[o].end()||*it>maxe) return -1;
if (l==r) return l;
int mid=(l+r)/2,ret=find(2*o+1,mid+1,r,mine,maxe);
if (ret!=-1) return ret;
return find(2*o,l,mid,mine,maxe);
}
int mid=(l+r)/2,ret=-1;
if (qr>mid) ret=find(2*o+1,mid+1,r,mine,maxe);
if (ret!=-1) return ret;
return find(2*o,l,mid,mine,maxe);
}
void updata(int o,int l,int r)
{
if (l==r) {minv[o]=min(minv[o],val);return ;}
int mid=(l+r)/2;
if (p<=mid) updata(2*o,l,mid);
else updata(2*o+1,mid+1,r);
minv[o]=min(minv[2*o],minv[2*o+1]);
}
int query(int o,int l,int r)
{
if (ql<=l&&r<=qr) return minv[o];
int mid=(l+r)/2,ret=1e9;
if (ql<=mid) ret=min(ret,query(2*o,l,mid));
if (qr>mid) ret=min(ret,query(2*o+1,mid+1,r));
return ret;
}
int main()
{
cin>>n;for (int i=1;i<=n;i++) scanf("%d",&a[i]);
build(1,1,n);
cin>>m;for (int i=1;i<=m;i++) scanf("%d%d",&qu[i].l,&qu[i].r),qu[i].i=i;
sort(qu+1,qu+m+1);
for (int i=0;i<maxn*4;i++) minv[i]=1e9;
int pos=1;
for (int i=2;i<=n;i++)
{
int j;
ql=1;qr=i-1;
j=find(1,1,n,a[i],1e9);
while (j!=-1)
{
int d=a[j]-a[i];
p=j;val=d;
updata(1,1,n);
if (!d||j==1) break;
d/=2;
ql=1;qr=j-1;
j=find(1,1,n,a[i],a[i]+d);
}
ql=1;qr=i-1;
j=find(1,1,n,0,a[i]);
while (j!=-1)
{
int d=a[i]-a[j];
p=j;val=d;
updata(1,1,n);
if (!d||j==1) break;
d/=2;
ql=1;qr=j-1;
j=find(1,1,n,a[i]-d,a[i]);
}
while (pos<=m&&qu[pos].r==i)
{
ql=qu[pos].l;qr=i;
ans[qu[pos].i]=query(1,1,n);
pos++;
}
}
for (int i=1;i<=m;i++) printf("%d\n",ans[i]);
return 0;
}