【题目链接】
【思路要点】
- 题目中提到的操作均为区间操作,考虑使用线段树。
- 注意到数列中的数始终非负,因此3号操作可以看做询问最小值以及其出现次数。
- 2号操作并非线段树的一般操作,无法直接支持标记合并。
- 考虑一个被2号操作定位的区间,记区间最小值为\(Min\),严格次小值为\(Mim\),对该区间实行操作数为\(Val\)的2号操作。
- 若\(Min+Val≥0\),显然这个操作可以看做一个区间加(减)操作。
- 若\(Min+Val<0\)且\(Mim+Val>0\),我们可以将这个操作看作是对区间中最小值的整体加(减)操作。
- 若\(Mim+Val≤0\),那么这就意味着最小值和严格次小值将在本次操作后相等,为了重新得到区间严格次小值,我们暴力对该区间在线段树上的两个子节点分别操作一次,并将信息合并。
- 用势能分析法能够证明,这样的做法存在时间复杂度下界\(O(MLog^{2}N)\),并且实际运行往往表现出优于该复杂度的效率。证明详见国家集训队2016论文集之《区间最值操作与历史最值问题——杭州学军中学 吉如一》。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 300005; const long long INF = 1e18; template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } struct info {long long Min, Mim; int Cnt; }; info operator + (info a, info b) { info ans; ans.Min = min(a.Min, b.Min); ans.Mim = min(a.Mim, b.Mim); ans.Cnt = 0; if (a.Min != ans.Min) ans.Mim = min(ans.Mim, a.Min); else ans.Cnt += a.Cnt; if (b.Min != ans.Min) ans.Mim = min(ans.Mim, b.Min); else ans.Cnt += b.Cnt; return ans; } struct SegmentTree { struct Node { int lc, rc, tag; long long val, delta; info now; } a[MAXN * 2]; int n, root, tot, value[MAXN]; void update(int root) { a[root].now = a[a[root].lc].now + a[a[root].rc].now; } void build(int &root, int l, int r) { root = ++tot; if (l == r) { a[root].now = (info) {value[l], INF, 1}; return; } int mid = (l + r) / 2; build(a[root].lc, l, mid); build(a[root].rc, mid + 1, r); update(root); } void init(int x) { n = x; tot = root = 0; build(root, 1, n); } void pushdown(); void cover(int root, int l, int r, long long val) { a[root].tag = 1; a[root].val = val; a[root].delta = 0; a[root].now = (info) {val, INF, r - l + 1}; } void cover(int root, int l, int r, int ql, int qr, long long val) { if (l == ql && r == qr) { cover(root, l, r, val); return; } pushdown(root, l, r); int mid = (l + r) / 2; if (mid >= ql) cover(a[root].lc, l, mid, ql, min(mid, qr), val); if (mid + 1 <= qr) cover(a[root].rc, mid + 1, r, max(mid + 1, ql), qr, val); update(root); } void cover(int l, int r, long long val) { cover(root, 1, n, l, r, val); } void add(int root, int l, int r, long long val) { if (a[root].tag == 1) { a[root].val += val; a[root].delta = 0; if (a[root].val < 0) a[root].val = 0; a[root].now.Min = a[root].val; } else { if (a[root].tag == 0) { a[root].tag = 2; a[root].val = 0; } a[root].val += val; a[root].now.Min += val; a[root].now.Mim += val; if (a[root].now.Min >= 0) return; if (a[root].now.Mim > 0) { a[root].delta -= a[root].now.Min; a[root].now.Min = 0; } else { pushdown(root, l, r); update(root); } } } void add(int root, int l, int r, int ql, int qr, long long val) { if (l == ql && r == qr) { add(root, l, r, val); return; } pushdown(root, l, r); int mid = (l + r) / 2; if (mid >= ql) add(a[root].lc, l, mid, ql, min(mid, qr), val); if (mid + 1 <= qr) add(a[root].rc, mid + 1, r, max(mid + 1, ql), qr, val); update(root); } void add(int l, int r, long long val) { add(root, 1, n, l, r, val); } info query(int root, int l, int r, int ql, int qr) { if (l == ql && r == qr) return a[root].now; pushdown(root, l, r); int mid = (l + r) / 2; if (ql > mid) return query(a[root].rc, mid + 1, r, ql, qr); if (mid >= qr) return query(a[root].lc, l, mid, ql, qr); return query(a[root].lc, l, mid, ql, mid) + query(a[root].rc, mid + 1, r, mid + 1, qr); } int query(int l, int r) { info tmp = query(root, 1, n, l, r); if (tmp.Min == 0) return tmp.Cnt; else return 0; } void pushdown(int root, int l, int r) { if (a[root].lc == 0) return; if (a[root].delta) { if (a[a[root].lc].now.Min == a[a[root].rc].now.Min) { a[a[root].lc].delta += a[root].delta; if (a[a[root].lc].tag == 1) a[a[root].lc].val += a[root].delta; else a[a[root].lc].now.Min += a[root].delta; a[a[root].rc].delta += a[root].delta; if (a[a[root].rc].tag == 1) a[a[root].rc].val += a[root].delta; else a[a[root].rc].now.Min += a[root].delta; } else if (a[a[root].lc].now.Min < a[a[root].rc].now.Min) { a[a[root].lc].delta += a[root].delta; if (a[a[root].lc].tag == 1) a[a[root].lc].val += a[root].delta; else a[a[root].lc].now.Min += a[root].delta; } else { a[a[root].rc].delta += a[root].delta; if (a[a[root].rc].tag == 1) a[a[root].rc].val += a[root].delta; else a[a[root].rc].now.Min += a[root].delta; } a[root].delta = 0; } int mid = (l + r) / 2; if (a[root].tag == 1) { cover(a[root].lc, l, mid, a[root].val); cover(a[root].rc, mid + 1, r, a[root].val); a[root].tag = 0; a[root].val = 0; } else if (a[root].tag == 2) { add(a[root].lc, l, mid, a[root].val); add(a[root].rc, mid + 1, r, a[root].val); a[root].tag = 0; a[root].val = 0; } update(root); } } ST; int main() { int n, m; read(n), read(m); for (int i = 1; i <= n; i++) read(ST.value[i]); ST.init(n); for (int i = 1; i <= m; i++) { int opt, l, r, val; read(opt), read(l), read(r); if (opt == 1) read(val), ST.cover(l, r, val); if (opt == 2) read(val), ST.add(l, r, val); if (opt == 3) writeln(ST.query(l, r)); } return 0; }