线段树维护单调栈
例题:给定一个排列 a,每个数有权值 f[i],维护一个单调递减栈的 f 之和,支持动态 swap 两个元素。
原理:


“push_up 的时候只更新右区间的 res,而不更新父亲的 res” 是最核心的思想!!!
constexpr int inf = 1e9;template<class Info>struct SegmentTree { vector<Info> info; int n; SegmentTree() : n(0) { } SegmentTree(int n_) { n = n_; info.assign(4 << __lg(n), Info()); } Z query(int p, int l, int r, int mi) { if (info[p].mi > mi) return {}; if (l == r) { return info[p].val; } int mid = l + r >> 1; if (info[p << 1].mi < mi) { return query(p << 1, l, mid, mi) + info[p << 1 | 1].res; } else { return query(p << 1 | 1, mid + 1, r, mi); } } void push_up(int p, int l, int r) { int mid = l + r >> 1; info[p].mi = min(info[p << 1].mi, info[p << 1 | 1].mi); info[p << 1 | 1].res = query(p << 1 | 1, mid + 1, r, info[p << 1].mi); } void modify(int p, int l, int r, int x, const Info &v) { if (l == r) { info[p] = v; return; } int mid = l + r >> 1; if (x <= mid) { modify(p << 1, l, mid, x, v); } else { modify(p << 1 | 1, mid + 1, r, x, v); } push_up(p, l, r); } void modify(int x, const Info &v) { modify(1, 1, n, x, v); } Z Getans() { return query(1, 1, n, inf); }};
struct Info { int mi = inf; Z res = 0; Z val = 0; // 这里非常重要!res 是考虑当前节点的左边兄弟的 min 时的答案,val 是每个叶子的原始 f 值,必须记录每个叶子原始的 f 值,才能在其再次出现在单调栈中的时候被统计上!};
void solve() { int n, q; cin >> n >> q; vector<Z> f(n + 1); for (int i = 1; i <= n; i++) { f[i] = f[i - 1] * i + i; } vector<int> a(n + 1); for (int i = 1; i <= n; i++) { cin >> a[i]; } Z tot = 0; for (int i = 1; i <= n; i++) { tot += f[i]; } SegmentTree<Info> seg(n); for (int i = 1; i <= n; i++) { seg.modify(i, {a[i], a[i], f[i], f[i]}); } cout << tot - seg.Getans() + n << "\n"; while (q--) { int x, y; cin >> x >> y; swap(a[x], a[y]); seg.modify(x, {a[x], a[x], f[x], f[x]}); seg.modify(y, {a[y], a[y], f[y], f[y]}); cout << tot - seg.Getans() + n << "\n"; }}