[NOI Online 2022 提高组] 丹钓战题解
首先我们可以得到这样一个性质:对于每一组询问 \([l,r]\),符合条件的 \([a_i,b_i]\) 一定且必须满足在它底下的元素编号 \(<l\)。
很容易理解,如果在它底下的元素编号 \(<l\),那么在 \([l,r]\) 就没有能够在它底下的元素,那么它就符合条件。
所以,我们就可以利用离线树状数组,以某个元素在栈中底下的元素标号从小到大加入树状数组,然后再计算出以元素标号 \(+1\) 为 \(l\) 的询问的答案。
时间复杂度为 \(O(nlogn)\)。
AC 代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 5;
int c[N], n, t, a[N], b[N], ans[N], lst[N];
struct node {
int l, r, idx;
} q[N];
stack<int> s;
vector<int> x[N], y[N];
int read() {
int num = 0, op = 1;
char ch = getchar();
while(ch < '0' || ch > '9') {
op = (ch == '-' ? -1 : 1);
ch = getchar();
}
while(ch >= '0' && ch <= '9') {
num = (num << 3) + (num << 1) + (ch ^ 48);
ch = getchar();
}
return num * op;
}
void upd(int x, int v) {
for(; x <= n; x += x & -x) c[x] += v;
}
int qry(int x) {
int res = 0;
for(; x; x -= x & -x) res += c[x];
return res;
}
int main() {
n = read(); t = read();
for(int i = 1; i <= n; i ++) a[i] = read();
for(int i = 1; i <= n; i ++) b[i] = read();
for(int i = 1; i <= t; i ++) {
q[i].l = read();
q[i].r = read();
q[i].idx = i;
y[q[i].l].push_back(i); //记录下左边界为 l 的询问
}
for(int i = 1; i <= n; i ++) {
while(!s.empty()) {
int p = s.top();
if(a[i] != a[p] && b[i] < b[p]) {
lst[i] = p;
x[lst[i]].push_back(i); //记录下底下编号为 lst[i] 的元素编号
break;
}
s.pop();
}
if(lst[i] == 0) x[0].push_back(i); //特判 lst[i]=0 的情况
s.push(i);
}
for(int i = 0; i <= n; i ++) {
for(int j = 0; j < x[i].size(); j ++)
upd(x[i][j], 1); //加入底下编号为 i 的元素
for(int j = 0; j < y[i + 1].size(); j ++) //计算左边界为 i+1 的询问
ans[q[y[i + 1][j]].idx] = qry(q[y[i + 1][j]].r) - qry(q[y[i + 1][j]].l - 1);
}
for(int i = 1; i <= t; i ++) cout << ans[i] << endl;
return 0;
}