关于此题[ABC379F] Buildings 2 单调栈加线段树二分的一些总结
-
题目大意:给定一个序列
表示房屋高度,规定当 满足 与 之间没有比 高的房屋时,在 房屋可以看见 房屋。现给定q个询问 ,问 后的同时能被 和 看见的房屋有多少 -
首先我们可以预处理出在每个房屋往右能看见多少房屋,这可以用单调栈实现。问题是如何回答询问。我们发现,如果想让
和 都能看见某一个 右的房屋,那么这个房屋一定要比 区间中的房屋最高的还要高。于是我们只需要维护一下 区间的最大值,然后在 往右的所有房屋中,找到第一个比这个最大值大的房屋高度即可,那么这个房屋的上一个房屋所预处理出的答案就是询问的答案。然而房屋的高度不是有序的,所以不能直接用二分求出第一个比该最大值大的房屋高度,我们可以用线段树二分来实现这个过程。用线段树维护区间最大值,当我们处在当前区间时,如果左儿子区间的最大值比询问的值大,那么就往左边找,不然就往右,如果左右两边的最大值都比查询的小,说明没有符合要求的。然而左儿子区间可能不完全在限制区间中,所以即使左儿子区间最大值比询问的值大也可能查询不到结果(可能最大值在限制区间的外面),此时也得查右区间。
#include<bits/stdc++.h>
using namespace std;
long long t;
const long long N = 5e5 + 10;
long long n,q;
long long h[N],tr[N],num[N],tmp[N],top;
long long maxn[N * 4];
void build(long long k,long long l,long long r) {
if(l == r) {
maxn[k] = h[l];
return;
}
long long mid = (l + r) >> 1;
build(k * 2,l,mid);
build(k * 2 + 1,mid + 1,r);
maxn[k] = max(maxn[k * 2],maxn[k * 2 + 1]);
}
long long _query(long long k,long long l,long long r,long long x,long long y,long long v) {
if(l == r) return l;
long long mid = (l + r) >> 1;
long long res = n + 1;
if(mid >= x && maxn[k * 2] > v) res = min(res,_query(k * 2,l,mid,x,y,v));
if(res != n + 1) return res;
if(mid < y && maxn[k * 2 + 1] > v) res = min(res,_query(k * 2 + 1,mid + 1,r,x,y,v));
return res;
}
long long lowbit(long long x) {
return x & (-x);
}
void add(long long lo,long long v) {
for(;lo <= n;lo += lowbit(lo))
tr[lo] = max(tr[lo],v);
}
long long query(long long l,long long r) {
long long res = 0;
while(l <= r) {
if(r - lowbit(r) >= l) {
res = max(res,tr[r]);
r -= lowbit(r);
}
else {
res = max(res,h[r]);
r--;
}
}
return res;
}
void solve() {
cin >> n >> q;
for(long long i = 1;i <= n;i++) cin >> h[i],add(i,h[i]);
build(1,1,n);
for(long long i = n;i >= 1;i--) {
num[i] = top;
while(top && tmp[top] < h[i]) top--;
tmp[++top] = h[i];
}
for(long long i = 1,l,r;i <= q;i++) {
cin >> l >> r;
long long maxn = query(l + 1,r);
long long key = _query(1,1,n,r + 1,n,maxn);
if(key == n + 1) cout << 0 << '\n';
else cout << num[key - 1] << '\n';
}
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
t = 1;
while(t--) solve();
return 0;
}