[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;
}
posted @ 2022-03-29 09:36  crp_cpp  阅读(133)  评论(0编辑  收藏  举报