51Nod - 1249 近似有序区间(单调栈,树状数组)

题目链接

题目大意

  略

解题思路

  如果一个子段合法,那么必然左边的值作为最小值的区域和右边的值作为最大值的区域都能包含彼此,所以用单调栈求出右边的值作为最大值能到达左边的下标的最小值\(l_i\),以及左边的值作为最小值能够到达右边的下标的最大值\(r_i\),然后将前者排序,按从左到右的顺序遍历每个数,将所有\(l_i < i\)的数的位置加入树状数组,查询在\([i, r_i]\)范围内有多少点即为当前点作为区间左端点的合法区间数量。

代码

const int maxn = 2e5+10;
const int maxm = 5e6+10;
int n, arr[maxn], c[maxn];
P l[maxn], r[maxn];
void add(int x) {
	while(x<=n) {
		++c[x];
		x += x&-x;
	}
}
int ask(int x) {
	int sum = 0;
	while(x) {
		sum += c[x];
		x -= x&-x;
	}
	return sum;
}
int main() {
	cin >> n;
	for (int i = 1 ; i<=n; ++i) scanf("%d", &arr[i]);
	stack<int> sk;
	for (int i = 1; i<=n; ++i) {
		while(!sk.empty() && arr[sk.top()]<=arr[i]) sk.pop();
		if (sk.empty()) l[i].x = 1;
		else l[i].x = sk.top()+1;
		l[i].y = i;
		sk.push(i);
	}
	while(!sk.empty()) sk.pop();
	for (int i = n; i>=1; --i) {
		while(!sk.empty() && arr[sk.top()]>=arr[i]) sk.pop();
		if (sk.empty()) r[i].x = n;
		else r[i].x = sk.top()-1;
		r[i].y = i;
		sk.push(i);
	}
	sort(l+1, l+n+1);
	int tot = 1; ll ans = 0;
	for (int i = 1; i<=n; ++i) {
		while(tot<=n && l[tot].x<=i) add(l[tot++].y);
		ans += ask(r[i].x)-ask(r[i].y-1);
	}
	cout << ans << endl;
    return 0;	
}
posted @ 2021-03-17 20:01  shuitiangong  阅读(67)  评论(0编辑  收藏  举报