题目
n(n<=3e5)个数,第i个数ai(1<=ai<=n),ai两两不同
q(q<=1e6)次询问,每次给出l,r(l<r),
询问[l,r]区间内,值域最接近的两个数,其值域距离是多少
即求l<=s<=t<=r,min(abs(a[s]-a[t]))的值
原题链接
思路来源
题解 CF765F 【Souvenirs】 - lhm_ 的博客 - 洛谷博客
题解
考虑将询问离线,固定右端点i,只考虑满足 的答案,
做一遍之后,再将 的值域翻转()后再做一遍就能得到所有情况。
对于某个右端点r,询问就是在树状数组上查询,
左端点l及l以后的每个位置j的答案的最小值,
所以每个位置j直接维护diff答案,树状数组上求后缀最小值
权值线段树查满足的最大位置j
所以,维护动态开点的权值线段树,
下标[1,n],维护的是a值,只查[ai,n]这一段区间的
值域[1,n],维护的是位置,查询某一段区间的最大位置
但一个一个往前跳找出所有的位置j,复杂度无法接受,还需进一步优化。
考虑当前位置为j,要想下一次找到的位置 j' 能更新答案,
其必须满足,
因为,点对 (j',j)也会包含在[j',i]的区间中,
所以,也更新过这个区间询问的答案
所以,要更新后缀最小值就必须满足这个式子。
移项得 ,发现差值每次减半,
具体来说,若a[pos]-a[i]为偶数,则除以2减1,
若为奇数直接除以2下取整,可以统一写做(a[pos]-a[i]-1)/2。
设值域为 V,因此在限制下往前跳的合法位置个数为
复杂度为 。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+10,M=1e6+10;
struct BitSuf{
static const int N=3e5+10,INF=0x3f3f3f3f;
int n,tr[N];
void init(int _n){
n=_n;
memset(tr,INF,(n+1)*sizeof(*tr));
}
void add(int x,int v){
for(int i=x;i;i-=i&-i)
tr[i]=min(tr[i],v);
}
int ask(int x){
int ans=INF;
for(int i=x;i<=n;i+=i&-i)
ans=min(ans,tr[i]);
return ans;
}
}tr;
struct DynamicSegmentTree{
static const int N=3e5+10;
int cnt;
struct node{
int l,r,mx;
void init(){l=r=mx=0;}
node(){init();}
}e[N*40];
void init(){
e[0].init();
cnt=0;
}
DynamicSegmentTree(){init();}
//1e5开*40,1e6开*45
//其实开(log(maxn)*(n+m)*maxn)就好
void newNode(int &cur){
if(!cur){
cur=++cnt;
e[cnt].init();
}
}
void upd(int l,int r,int &cur,int pos,int v){
newNode(cur);
e[cur].mx=max(e[cur].mx,v);
if(l==r)return;
int mid=(l+r)/2;
if(pos<=mid)upd(l,mid,e[cur].l,pos,v);
else upd(mid+1,r,e[cur].r,pos,v);
}
int ask(int l,int r,int cur,int ql,int qr){
if(!cur)return 0;
if(ql<=l&&r<=qr)return e[cur].mx;
int ans=0,mid=(l+r)/2;
if(ql<=mid)ans=max(ans,ask(l,mid,e[cur].l,ql,qr));
if(qr>mid)ans=max(ans,ask(mid+1,r,e[cur].r,ql,qr));
return ans;
}
}tr2;
int n,m,l,r,a[N],ans[M];
vector<tuple<int,int> >q[N];
void sol(){
tr.init(n);
tr2.init();
int rt=0;
for(int i=1;i<=n;++i){
int pos=tr2.ask(1,n,rt,a[i],n);
while(pos){
tr.add(pos,a[pos]-a[i]);
pos=tr2.ask(1,n,rt,a[i],(a[i]+a[pos]-1)/2);
}
tr2.upd(1,n,rt,a[i],i);
for(auto &[l,id]:q[i]){ //后缀min
ans[id]=min(ans[id],tr.ask(l));
}
}
}
int main(){
scanf("%d%d",&n,&m);
tr.init(n);
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
}
for(int i=1;i<=m;++i){
scanf("%d%d",&l,&r);
q[r].push_back({l,i});
ans[i]=n;
}
sol();
for(int i=1;i<=n;++i){
a[i]=n+1-a[i];
}
sol();
for(int i=1;i<=m;++i){
printf("%d\n",ans[i]);
}
return 0;
}