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;
}