线段树维护单调栈——区间查询版本 & 维护递减序列
这次算是完全搞懂了吧()()(
其实追溯到了单调栈的一些本质问题,而且搬到笛卡尔树上特别清楚了。
你线段树维护单调栈上升/下降,本质上是维护笛卡尔树里面一个点/根的最长左链/右链。
#include <bits/stdc++.h>
#define raed read
#define cacl calc
#define pb push_back
#define pii pair <int, int>
#define int long long
#define fi first
#define se second
#define ls p << 1
#define rs p << 1 | 1
using namespace std;
const int N = 2e5 + 5, M = 20; int read();
int n, la[N], lb[N];
struct node {
int x, y, id;
} p[N];
int ans;
struct sT {
int maxn, sum;
} T[N << 2];
int get(int p, int l, int r, int x) { // 选的数 > x
if(l > r) return 0;
if(T[p].maxn <= x) return 0;
if(l == r) return T[p].maxn > x; int mid = l + r >> 1;
if(T[rs].maxn <= x) return get(ls, l, mid, x);
return get(rs, mid + 1, r, x) + T[p].sum - T[rs].sum;
}
void update(int p, int k, int x, const int l = 1, const int r = n) {
if(l == r) return T[p].sum = 1, T[p].maxn = x, void();
int mid = l + r >> 1;
if(k <= mid) update(ls, k, x, l, mid);
else update(rs, k, x, mid + 1, r);
T[p].maxn = max(T[ls].maxn, T[rs].maxn);
int sb = get(ls, l, mid, T[rs].maxn);
T[p].sum = T[rs].sum + sb;
}
int now;
int query(int p, int ql, int qr, const int l = 1, const int r = n) {
if(ql <= l and r <= qr) {
int w = get(p, l, r, now);
now = max(now, T[p].maxn);
return w;
}
int mid = l + r >> 1, ans = 0;
if(qr > mid) ans += query(rs, ql, qr, mid + 1, r);
if(ql <= mid) ans += query(ls, ql, qr, l, mid);
return ans;
}
void solve() {
cin >> n;
for(int i = 1;i <= n; ++i) p[i].x = read(), p[i].y = read();
sort(p + 1, p + n + 1, [&](node a, node b){ return a.x < b.x; });
for(int i = 1;i <= n; ++i) la[i] = p[i].x, lb[i] = p[i].y;
sort(la + 1, la + n + 1); sort(lb +1 , lb + n + 1);
for(int i = 1;i <= n; ++i)
p[i].y = lower_bound(lb + 1, lb + n + 1, p[i].y) - lb, p[i].id = i;
sort(p + 1, p + n + 1, [&](node a, node b){ return a.y < b.y; });
for(int i = 1;i <= n; ++i) {
now = 0;
ans += query(1, 1, p[i].id);
update(1, p[i].id, i);
}
cout << ans << "\n";
}
signed main() {
int T = 1;
while(T--) solve();
return 0;
}
/*
对当前点前缀 <= y 的点做单调栈
类似线段树维护单调栈考虑转换到 CDQ 上,每个点维护当前答案和前缀最小值
你可以找到排序后的那个位置
按照 y 的大xiao动态加,每次计算答案即可
线段树维护单调栈怎么做
*/
int read() {
char c; int f = 1, sum = 0;
while(c < '0' or c > '9') {if(c == '-') f = -1;c = getchar();}
while(c >= '0' and c <= '9') {sum = (sum << 3) + (sum << 1) + (c ^ 48);c = getchar();}
return sum * f;
}