ABC379Fsol
upd:修正了原文与注释的一点细节错误和示意图错误。
这里提供一种不需要高级数据结构的在线做法。
我们发现每个点向右看能看到的,且比自己高的建筑数量是可以用单调栈线性求的,可以先预处理出来,设为
对于每一个询问,考虑
如果
但如果
我们看一张示意图:
我们发现,答案所求长度的序列一定是
所以我们的问题就变成了:在
这个问题可以使用倍增解决。记录每个建筑向右看到且不比自己低的建筑序列中,第
这里有一个坑点,就是一定要输出目标后缀的前一个建筑对应的答案,因为一个建筑向右看到的建筑是不包含它本身的。
ACcode
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,Q,lg[200005],f[200005][20],g[200005][20];
int h[200005],rs[200005];
int R,q[200005];
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>Q;lg[1]=0;
for (int i=2;i<=n;i++)lg[i]=lg[i>>1]+1;
for(int i=1;i<=n;i++)cin>>h[i],f[i][0]=h[i];
h[0]=1e9+7;//这是哨兵
for(int i=n;i>=1;i--){
while(R>0&&h[q[R]]<h[i])R--;//队列里存的是编号!
rs[i]=R;
g[i][0]=q[R];//记录右侧第一个不小于自己的
q[++R]=i;
}//单调栈预处理rs[i]
for (int j=1;j<=lg[n];j++)
for (int i=1;i<=n-(1<<j)+1;i++)
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]),
g[i][j]=g[g[i][j-1]][j-1];//跳ST表和倍增
while(Q--){
int l,r;cin>>l>>r;
l++;
int len=lg[r-l+1];
int mx=max(f[l][len],f[r-(1<<len)+1][len]);//区间最大值
if(mx==h[r]){
cout<<rs[r]<<endl;
continue;
}
for(int i=18;i>=0;i--)
if(h[g[r][i]]<mx)
r=g[r][i];//直接跳
cout<<rs[r]<<endl;
}
return 0;
}