CF977E Good Subsegments 题解

题意:给一个排列,求值域连续段的数量,\(n\le 5\cdot 10^5\).

Solution

这道题很 educational!

首先这样的题第一是离线 + 扫描线,第二是莫队.

具体就是按右端点离线下来,我们动态加一个元素(移动右端点),维护当前所有左端点代表的区间的权值答案,查询某个右端点的答案就相当于求区间和.

然后首先 \([l,r]\) 的合法条件为 \(max(l,r)-min(l,r)-(r-l)=0\),而显然总有 \(max(l,r)-min(l,r)-(r-l)\ge 0\).

于是考虑用线段树维护 \(max(l,r)-min(l,r)-(r-l)\),考虑如何维护添加一个元素,\(max\)\(min\) 可以用单调栈变成区间加减,其他的修改是平凡的;

而某个右端点的答案相当于最小值为 0 时的最小值个数,这是可以维护的.

但是我们问的是一个区间内所有子区间,而不仅是右端点为 \(r\) 的区间

其实就是每个点记录一个答案,每次做完修改后,都把每个点的“最小值个数”加到答案里面

这个操作相当于区间修改,可以用标记来维护,也就是加“最小值个数”的系数!

然后就做完了,\(O(n\log n)\).

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N = 2e5 + 10;

int n, q, a[N];

vector<pair<int, int>> query[N];
ll Ans[N];

int mn[N << 2], mncnt[N << 2], add[N << 2];
int cntadd[N << 2];
ll ans[N << 2];

#define lc (u << 1)
#define rc ((u << 1) | 1)
#define mid ((l + r) >> 1)

void up(int u) {
	mn[u] = min(mn[lc], mn[rc]);
	mncnt[u] = 0;
	if (mn[lc] == mn[u]) mncnt[u] += mncnt[lc];
	if (mn[rc] == mn[u]) mncnt[u] += mncnt[rc];
	ans[u] = ans[lc] + ans[rc];
}
void updadd(int u, int v) {
	mn[u] += v;
	add[u] += v;
}
void updcntadd(int u, int v) {
	cntadd[u] += v;
	ans[u] += 1ll * mncnt[u] * v;
}
void down(int u) {
	if (add[u]) updadd(lc, add[u]), updadd(rc, add[u]), add[u] = 0;
	if (cntadd[u]) {
		if (mn[lc] == mn[u]) updcntadd(lc, cntadd[u]);
		if (mn[rc] == mn[u]) updcntadd(rc, cntadd[u]);
		cntadd[u] = 0;
	}
}

void build(int u, int l, int r) {
	mn[u] = l, mncnt[u] = 1, add[u] = cntadd[u] = 0, ans[u] = 0;
	if (l == r) {
		return;
	}
	build(lc, l, mid), build(rc, mid + 1, r), up(u);
}
void upd(int u, int l, int r, int x, int y, int v) {
	if (y < l || r < x) return;
	if (x <= l && r <= y) return updadd(u, v);
	down(u), upd(lc, l, mid, x, y, v), upd(rc, mid + 1, r, x, y, v), up(u);
}
ll qry(int u, int l, int r, int x, int y) {
	if (y < l || r < x) return 0;
	if (x <= l && r <= y) return ans[u];
	return down(u), qry(lc, l, mid, x, y) + qry(rc, mid + 1, r, x, y);
}

#undef lc
#undef rc
#undef mid

int tp1, Mxstk[N];
int tp2, Mnstk[N];

int main() {
//	freopen("cat.in", "r", stdin);
//	freopen("cat.out", "w", stdout);
	ios::sync_with_stdio(false), cin.tie(nullptr);
	cin >> n;
	for (int i = 1; i <= n; ++i) cin >> a[i];
	cin >> q;
	for (int i = 1, l, r; i <= q; ++i) 
		cin >> l >> r, query[r].push_back(make_pair(i, l));
	build(1, 1, n);
	for (int i = 1; i <= n; ++i) {
		updadd(1, -1);
		while (tp1 && a[Mxstk[tp1]] <= a[i]) {
			upd(1, 1, n, Mxstk[tp1 - 1] + 1, Mxstk[tp1], a[i] - a[Mxstk[tp1]]);
			--tp1;
		}
		Mxstk[++tp1] = i;
		while (tp2 && a[Mnstk[tp2]] >= a[i]) {
			upd(1, 1, n, Mnstk[tp2 - 1] + 1, Mnstk[tp2], -(a[i] - a[Mnstk[tp2]]));
			--tp2;
		}
		Mnstk[++tp2] = i;
		updcntadd(1, 1);
		for (auto now : query[i]) Ans[now.first] = qry(1, 1, n, now.second, i);
	}
	for (int i = 1; i <= q; ++i) cout << Ans[i] << '\n';
	return 0;
}
posted @ 2024-07-31 21:12  Laijinyi  阅读(1)  评论(0编辑  收藏  举报