CF765F Souvenirs
Link
先看暴力做法。
把询问按右端点升序排序。
对于每个\(r\),我们处理出左端点\(l\in[1,r)\)的所有答案\(ans_l\),当\(r\)向右扩展时暴力修改\(ans\)。
这样子显然不能过,我们考虑优化。
我们可以认为\(ans_l=\min\limits_{i=l}^{r-1}ans_i\),因此显然\(ans\)是单调不减的。
如果我们把单点查询改成区间查询最小值,那么我们就可以在修改时不修改每个\(ans\),只需要修改一部分\(ans\)。
我们选择线段树维护。
具体而言,当把\(r\)扩展到\(r+1\)时,我们在线段树上从右往左修改\(ans\),如果我们修改了某个\(ans\),且当前的答案为\(mn\),那么在之后的修改时,如果某个节点(也就是这个节点左边的节点)的贡献小于\(mn\),那么就没有必要继续递归进入其子节点了。
如何更新某个线段树节点的答案呢?在每个线段树的节点开个vector记录这个节点代表的区间内的所有数,更新时二分一下即可。
#include<bits/stdc++.h>
#define IT vector<int>::iterator
#define pb push_back
#define ls p<<1
#define rs p<<1|1
#define mid ((l+r)>>1)
using namespace std;
namespace IO
{
char ibuf[(1<<21)+1],obuf[(1<<21)+1],st[20],*iS,*iT,*oS=obuf,*oT=obuf+(1<<21);
char Get(){return (iS==iT? (iT=(iS=ibuf)+fread(ibuf,1,(1<<21)+1,stdin),(iS==iT? EOF:*iS++)):*iS++);}
void Flush(){fwrite(obuf,1,oS-obuf,stdout),oS=obuf;}
void Put(char x){*oS++=x;if(oS==oT)Flush();}
int read(){int x=0;char ch=Get();while(ch>57||ch<48)ch=Get();while(ch>=48&&ch<=57)x=x*10+(ch^48),ch=Get();return x;}
void write(int x){int top=0;if(!x)Put('0');while(x)st[++top]=(x%10)+48,x/=10;while(top)Put(st[top--]);Put('\n');}
}
using namespace IO;
int min(int a,int b){return a<b? a:b;}
const int N=200007,M=300007,INF=1e9;
int a[N],ans[N<<2],Ans[N],n,m,mn;
vector<int>vec[N<<2];
struct node{int l,r,id;}q[M];
int operator<(node a,node b){return a.r<b.r;}
void build(int p,int l,int r)
{
for(int i=l;i<=r;++i) vec[p].pb(a[i]);
sort(vec[p].begin(),vec[p].end()),ans[p]=INF;
if(l==r) return;
build(ls,l,mid),build(rs,mid+1,r);
}
int query(int p,int l,int r,int L,int R)
{
if(L==l&&r==R) return ans[p];
return R<=mid? query(ls,l,mid,L,R):(L>mid? query(rs,mid+1,r,L,R):min(query(ls,l,mid,L,mid),query(rs,mid+1,r,mid+1,R)));
}
void update(int p,int l,int r,int f,int x)
{
if(r<=f)
{
IT it=upper_bound(vec[p].begin(),vec[p].end(),x);
if(it!=vec[p].end()) ans[p]=min(ans[p],*it-x);
if(it!=vec[p].begin()) ans[p]=min(ans[p],x-*(it-1));
if(mn<=ans[p]) return;
if(l==r) return (void)(mn=min(mn,ans[p]));
}
if(f>mid) update(rs,mid+1,r,f,x);
update(ls,l,mid,f,x),ans[p]=min(ans[p],min(ans[ls],ans[rs])),mn=min(mn,ans[p]);
}
int main()
{
int i,j;n=read();
for(i=1;i<=n;++i) a[i]=read();
build(1,1,n),m=read();
for(i=1;i<=m;++i) q[i].l=read(),q[i].r=read(),q[i].id=i;
sort(q+1,q+m+1);
for(j=1,i=2;i<=n;++i)
{
mn=INF,update(1,1,n,i-1,a[i]);
for(;j<=m&&q[j].r<=i;++j) Ans[q[j].id]=query(1,1,n,q[j].l,i);
}
for(i=1;i<=m;++i) write(Ans[i]);
return Flush(),0;
}