传送门
shallwe’s blog //以上内容由shallwe本人暴力添加
思路:
好题
断断续续想了1周左右
暴力思路显然是
O(n2logn)
的
我最开始的想法是用莫队+set来搞
但是发现即使是在随机数列下,
O(nn√logn)
的复杂度也是不科学的
那怎么办?
(下列做法可能比较难想……)
如果说我们处理出当前区间中每个元素往前的最小差值,那不就可以得到答案了吗?
再深入思考一下
对于
a[j]>a[k]>x
,
j<k
,
a[j]>a[k]>x,j<k
,
a[j]
不贡献答案;
a[j]<a[k]<x
时同理
那么对于每一个元素x来说,我们只要在前面找小于a[x]的递增序列和大于x的递减序列就可以了
因为只有它们才会在x存在时贡献答案
然后我们这里引入一个奥妙重重的性质
对于长度为n的随机数列中的某个元素a[x],下标小于x的数构成的小(大)于a[x]的递减(增)序列长度为 logn
对于这道题来说,这个性质是极好的
这样就相当于每次找某个权值区间中位置小于某个值的最大位置了
但这样并不好找
因为当前点后面的数会对我们查询产生影响
那就从左向右扫着做,后面数就不会有影响了
这样就相当于找某个权值区间中的最大位置了
这就很好办了啊
权值线段树就可以了
操作是单点修改和区间求max
那每次求出的答案的贡献区间是什么呢?
如果说当前点为r,找到的贡献点是l,那么它对答案的贡献区间是左端点在
[1,l]
,右端点在
[r,n]
的询问
这不是很好维护
所以我们干脆把询问离线下来,以右端点为关键字从小到大排序,然后把所有的答案都记到左端点上,这样就可以拿一颗新的线段树来维护了
每次修改的区间就是
[1,l]
操作是区间修改和区间求min
总结一下就是随机数列的神奇性质+离线+扫描线思想+双线段树维护
代码还是挺好写的
题解好难写啊
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#define M 100005
#define up 2147483647
using namespace std;
int n,m;
int a[M],b[M],ha[M],S[M<<2],V[M<<2],lazy[M<<2],ans[M];
struct query
{
int id,l,r;
}q[M];
int in()
{
int t=0;char ch=getchar();bool f=0;
while (ch>'9'||ch<'0') f=(ch=='-'),ch=getchar();
while (ch>='0'&&ch<='9') t=(t<<1)+(t<<3)+ch-48,ch=getchar();
return f?-t:t;
}
bool cmp(query a,query b)
{
return a.r<b.r;
}
int V_get(int rt,int begin,int end,int l,int r)
{
if (l<=begin&&end<=r) return V[rt];
int mid=begin+end>>1,ans=0;
if (mid>=l) ans=max(ans,V_get(rt<<1,begin,mid,l,r));
if (mid<r) ans=max(ans,V_get(rt<<1|1,mid+1,end,l,r));
return ans;
}
void V_update(int rt,int begin,int end,int pos,int val)
{
if (begin==end) return void(V[rt]=val);
int mid=begin+end>>1;
if (mid>=pos) V_update(rt<<1,begin,mid,pos,val);
else V_update(rt<<1|1,mid+1,end,pos,val);
V[rt]=val;
}
void S_build(int rt,int begin,int end)
{
S[rt]=up;
if (begin==end) return;
int mid=begin+end>>1;
S_build(rt<<1,begin,mid);
S_build(rt<<1|1,mid+1,end);
}
void pushdown(int rt)
{
if (!lazy[rt]) return;
S[rt<<1]=min(S[rt<<1],lazy[rt]);
if (!lazy[rt<<1]) lazy[rt<<1]=lazy[rt];
else lazy[rt<<1]=min(lazy[rt],lazy[rt<<1]);
S[rt<<1|1]=min(S[rt<<1|1],lazy[rt]);
if (!lazy[rt<<1|1]) lazy[rt<<1|1]=lazy[rt];
else lazy[rt<<1|1]=min(lazy[rt],lazy[rt<<1|1]);
lazy[rt]=0;
}
void S_update(int rt,int begin,int end,int l,int r,int val)
{
if (l<=begin&&end<=r)
{
S[rt]=min(S[rt],val);
if (!lazy[rt]) lazy[rt]=val;
else lazy[rt]=min(val,lazy[rt]);
return;
}
pushdown(rt);
int mid=begin+end>>1;
if (mid>=l) S_update(rt<<1,begin,mid,l,r,val);
if (mid<r) S_update(rt<<1|1,mid+1,end,l,r,val);
S[rt]=min(S[rt<<1],S[rt<<1|1]);
}
int S_get(int rt,int begin,int end,int l,int r)
{
if (l<=begin&&end<=r) return S[rt];
pushdown(rt);
int mid=begin+end>>1,ans=up;
if (mid>=l) ans=min(ans,S_get(rt<<1,begin,mid,l,r));
if (mid<r) ans=min(ans,S_get(rt<<1|1,mid+1,end,l,r));
return ans;
}
void cal(int x)
{
int y=1,val=b[0],ans=up;
for (;val>a[x]&&y;)
{
y=V_get(1,1,b[0],a[x],val);
if (y&&a[y]!=a[x])
ans=min(ans,ha[a[y]]-ha[a[x]]),
S_update(1,1,n,1,y,ha[a[y]]-ha[a[x]]);
if (!y) break;
val=a[y]-1;
}
val=1;y=1;
for (;val<a[x]&&y;)
{
y=V_get(1,1,b[0],val,a[x]);
if (y&&a[y]!=a[x])
ans=min(ans,ha[a[x]]-ha[a[y]]),
S_update(1,1,n,1,y,ha[a[x]]-ha[a[y]]);
if (!y) break;
val=a[y]+1;
}
V_update(1,1,b[0],a[x],x);
}
main()
{
n=in();m=in();
for (int i=1;i<=n;++i) b[i]=a[i]=in();
sort(b+1,b+n+1);
b[0]=unique(b+1,b+n+1)-b-1;
for (int i=1;i<=b[0];++i) ha[i]=b[i];
for (int i=1;i<=n;++i)
a[i]=lower_bound(b+1,b+b[0]+1,a[i])-b;
for (int i=1;i<=m;++i)
q[i]=(query){i,in(),in()};
sort(q+1,q+m+1,cmp);
int last=0;
S_build(1,1,n);
for (int i=1;i<=m;++i)
{
for (int j=last+1;j<=q[i].r;++j) cal(j);
ans[q[i].id]=S_get(1,1,n,q[i].l,q[i].r);
last=q[i].r;
}
for (int i=1;i<=m;++i) printf("%d\n",ans[i]);
}