题目大意
给定一个序列
多次询问一个区间最接近两个数的差值
最接近的两个数可以相同,但不能是同一个位置上的数。
允许离线
强大线段树做法
我们首先可以扫描线
从左到右扫,每次处理右端点在扫描线上的所有询问。
我们维护一颗线段树,线段树每个节点的值没有太多实际的意义,但它需要满足一个性质:
假如当前扫描线在now,那么在线段树中查询[l,now]这个区间的答案就是正确的答案
听起来很不可思议?
这样每次询问直接询问即可
我们思考每次从now-1到now,线段树哪些信息需要更改,此时我们加入了一个a[now],设为d。
我们可以先去定位区间[1,now],规定优先走右儿子。
对于一个被定位的区间[L,R]满足R<=now,什么情况下不需要在对它包括它子树的信息进行更改了呢?
因为我们优先走右儿子,那么我们可以维护出一个mi表示此时[R+1,now]的答案。
假如对于[L,R],d在[L,R]中找到的每一个数t(注意如果R=now,t并不能去a[now]),都有
|d−t|>=mi
,那么这个点上的值不更改也可以满足我们线段树需要满足的条件,即询问[i,now]得到正确的答案。于是我们更新mi后直接退出。
否则的话,我们可能要更改这个区间上的值,我们暴力递归左右,这里也是优先走右边。
为了验证是否还需要往下做,每个区间维护一个set,每次给d找前驱和后继即可。
因为不能包含a[now],所以每次做完修改再把now所处的区间的set里都塞一个a[now]。
这样做正确性是没有问题的,时间复杂度如何呢?
我们只需要考虑一直递归到叶子的位置,其他的都是区间,区间定位的复杂度会和递归到叶子保持一致(假设相邻两个递归到叶子的位置i和j,那么在到i叶子路径和到j叶子路径第一次分开后,i那条每往左走右边那个区间被定位为[i+1,j-1]里的直接退出,j那条同理,可以看出两者复杂度同阶)。
一个位置i什么时候会被递归到叶子,显然是[i,now]的答案比[i+1,now]优秀。考虑最坏情况,就是序列1 n n/2 n/4 n/8……每次值会减一半,可以分析出一个位置只会被递归到叶子log次!
算上set的复杂度,感觉应该是三个log?实际跑起来会比较玄学。
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<set>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=100000+10,maxm=300000+10,inf=2000000000;
struct dong{
int l,r,id;
} ask[maxm];
int tree[maxn*4],ans[maxm],a[maxn],sta[80];
set<int> e[maxn*4];
int i,j,k,l,r,t,n,m,mi,top;
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;
}
bool cmp(dong a,dong b){
return a.r<b.r;
}
void build(int p,int l,int r){
tree[p]=inf;
if (l==r) return;
int mid=(l+r)/2;
build(p*2,l,mid);build(p*2+1,mid+1,r);
}
void change(int p,int l,int r,int qr,int d){
if (l==r){
if (l==qr) return;
tree[p]=min(tree[p],abs(d-a[l]));
mi=min(mi,tree[p]);
return;
}
if (r<=qr){
set<int>::iterator it=e[p].lower_bound(d);
if ((it==e[p].end()||abs(*it-d)>=mi)&&(it==e[p].begin()||abs(*(--it)-d)>=mi)){
mi=min(mi,tree[p]);
return;
}
int mid=(l+r)/2;
change(p*2+1,mid+1,r,qr,d);change(p*2,l,mid,qr,d);
tree[p]=min(tree[p*2],tree[p*2+1]);
return;
}
int mid=(l+r)/2;
if (qr<=mid) change(p*2,l,mid,qr,d);
else{
change(p*2+1,mid+1,r,qr,d);
change(p*2,l,mid,qr,d);
}
tree[p]=min(tree[p*2],tree[p*2+1]);
}
void cr(int p,int l,int r,int a,int b){
e[p].insert(b);
if (l==r) return;
int mid=(l+r)/2;
if (a<=mid) cr(p*2,l,mid,a,b);else cr(p*2+1,mid+1,r,a,b);
}
int query(int p,int l,int r,int a,int b){
if (l==a&&r==b) return tree[p];
int mid=(l+r)/2;
if (b<=mid) return query(p*2,l,mid,a,b);
else if (a>mid) return query(p*2+1,mid+1,r,a,b);
else return min(query(p*2,l,mid,a,mid),query(p*2+1,mid+1,r,mid+1,b));
}
void write(int x){
if (!x){
putchar('0');
putchar('\n');
return;
}
top=0;
while (x){
sta[++top]=x%10;
x/=10;
}
while (top){
putchar('0'+sta[top]);
top--;
}
putchar('\n');
}
int main(){
//freopen("data.in","r",stdin);
n=read();
fo(i,1,n) a[i]=read();
build(1,1,n);
m=read();
fo(i,1,m) ask[i].l=read(),ask[i].r=read(),ask[i].id=i;
sort(ask+1,ask+m+1,cmp);
r=0;
fo(i,1,m){
while (r<ask[i].r){
r++;
mi=inf;
change(1,1,n,r,a[r]);
cr(1,1,n,r,a[r]);
}
ans[ask[i].id]=query(1,1,n,ask[i].l,r);
}
fo(i,1,m) write(ans[i]);
}