diff --git "a/2-\346\225\260\346\215\256\347\273\223\346\236\204.md" "b/2-\346\225\260\346\215\256\347\273\223\346\236\204.md" index e50c960..b269e93 100644 --- "a/2-\346\225\260\346\215\256\347\273\223\346\236\204.md" +++ "b/2-\346\225\260\346\215\256\347\273\223\346\236\204.md" @@ -242,354 +242,210 @@ class SparseTable ## 带懒标记线段树 ```cpp -const int N = 1e6; -int a[N]; -int tag[4 * N]; -int tree[4 * N]; -int n; -void push_up(int p) +#include +using std::vector; +class SegTree { - tree[p] = tree[ls(p)] + tree[rs(p)]; -} -void build(int p, int l, int r) -{ - if (l == r) + private: + struct node { - tree[p] = a[l]; - return; - } - int mid = (l + r) >> 1; - build(ls(p), l, mid); - build(rs(p), mid + 1, r); - push_up(p); -} -void push_down(int p, int l, int r) -{ - int mid = (l + r) >> 1; - tag[ls(p)] += tag[p]; - tag[rs(p)] += tag[p]; - tree[ls(p)] += tag[p] * (mid - l + 1); - tree[rs(p)] += tag[p] * (r - mid); - tag[p] = 0; -} -void update(int nl, int nr, int k, int p = 1, int l = 1, int r = n) -{ - if (nl <= l && r <= nr) - { - tag[p] += k; - tree[p] += k * (r - l + 1); - return; - } - push_down(p, l, r); - int mid = (l + r) >> 1; - if (nl <= mid) - update(nl, nr, k, ls(p), l, mid); - if (nr > mid) - update(nl, nr, k, rs(p), mid + 1, r); - push_up(p); -} -int query(int x, int y, int l = 1, int r = n, int p = 1) -{ - int res = 0; - if (x <= l && y >= r) - return tree[p]; - int mid = (l + r) >> 1; - push_down(p, l, r); - if (x <= mid) - res += query(x, y, l, mid, ls(p)); - if (y > mid) - res += query(x, y, mid + 1, r, rs(p)); - return res; -} -int main() -{ - int q; - cin >> n >> q; - for (int i = 1; i <= n; i++) - cin >> a[i]; - build(1, 1, n); - while (q--) - { - int op, x, y, k; - cin >> op; - if (op == 1) - { - cin >> x >> y >> k; - update(x, y, k); - } - else - { - cin >> x >> y; - cout << query(x, y) << endl; - } + long long l, r, v, t; + }; + int size = 1e6 + 5; + vector f; + inline int ls(int p) + { + return p << 1; } - return 0; -} -``` -## 线段树上二分 -eg. 两种操作,1. 修改ai为d 2. 查询l,r中第一次出现大于等于d位置,否则返回-1 - -维护区间最大值, - -对一个区间判断最大值是否大于等于d - -存在则先找左区间,再找右区间 - -## 区间最值线段树 - -```cpp -struct node -{ - int maxn; -} tree[800005]; -int n; -int tag[800005]; -void push_down(int p, int l, int r) -{ // 标记下压 - int mid = (r + l) / 2; - tree[2 * p].maxn += tag[p]; - tree[2 * p + 1].maxn += tag[p]; - tag[2 * p] += tag[p]; - tag[2 * p + 1] += tag[p]; - tag[p] = 0; -} -void update(int l, int r, int k, int cl = 1, int cr = n, int p = 1) -{ // 更新 - if (cl > r || cr < l) + inline int rs(int p) { - return; + return p << 1 | 1; } - if (cl >= l && cr <= r) + + void build(int pos, int l, int r, vector &a) { - tree[p].maxn += k; - if (cl < cr) + f[pos] = {l, r, 0, 0}; + if (l == r) { - tag[p] += k; + f[pos].v = a[l]; + return; } + int mid = ((l + r) >> 1); + build(ls(pos), l, mid, a); + build(rs(pos), mid + 1, r, a); + push_up(pos); } - else + void push_up(int pos) { - int mid = (cl + cr) >> 1; - push_down(p, cl, cr); - if (l <= mid) - update(l, r, k, cl, mid, 2 * p); - if (r > mid) - update(l, r, k, mid + 1, cr, 2 * p + 1); - tree[p].maxn = max(tree[p << 1].maxn, tree[p * 2 + 1].maxn); + // modify here + f[pos].v = f[ls(pos)].v + f[rs(pos)].v; } -} -int query(int l, int r, int cl = 1, int cr = n, int p = 1) -{ // 查询 - if (cl >= l && cr <= r) + inline void push_down(long long pos) { - return tree[p].maxn; + if (!f[pos].t) + return; + f[ls(pos)].t += f[pos].t; + f[rs(pos)].t += f[pos].t; + int mid = (f[pos].l + f[pos].r) / 2; + f[ls(pos)].v += f[pos].t * (mid - f[ls(pos)].l + 1); // modify here + f[rs(pos)].v += f[pos].t * (f[rs(pos)].r - mid); // modify here + f[pos].t = 0; } - else + + public: + SegTree(long long s, vector &ori) : size(s) { - int mid = (cl + cr) >> 1; - push_down(p, cl, cr); - int tmp = 0; - if (l <= mid) - tmp = max(tmp, query(l, r, cl, mid, 2 * p)); - if (r > mid) - tmp = max(tmp, query(l, r, mid + 1, cr, 2 * p + 1)); - return tmp; + f = vector(s << 2); + build(1, 1, s, ori); } -} -``` -## 单调栈 -满足栈中元素单调递增或递减的栈 - -可用于o(n)寻找每个数之后第一个大于他的数的位置(用单调递减栈) - -可解决求 $max_{1\leq l\leq n,l\leq r \leq n}((r-l+1)*min_{l\leq i \leq r}a[i])$ - -即区间长度乘区间最值结果的最值 - -``` cpp -int n,m;//洛谷P5788 -stackst; -int a[3000005],ans[3000005]; -void solve(){ - cin>>n; - for(int i=1;i<=n;i++) cin>>a[i]; - for(int i=1;i<=n;i++){ - while(st.size()&&a[i]>a[st.top()]){//维护递减栈 - ans[st.top()] = i;//更新答案 - st.pop(); - } - st.push(i); + long long query(int pos, int l, int r) + { + push_down(pos); + if (f[pos].l >= l && f[pos].r <= r) + return f[pos].v; + int mid = (f[pos].l + f[pos].r) / 2; + if (l > mid) + return query(rs(pos), l, r); + else if (r <= mid) + return query(ls(pos), l, r); + else + // modify here + return query(ls(pos), l, mid) + query(rs(pos), mid + 1, r); } - for(int i=1;i<=n;i++) cout<= l && f[pos].r <= r) + { + f[pos].t += k; + f[pos].v += k * (r - l + 1); + return; + } + push_down(pos); + int mid = (f[pos].l + f[pos].r) / 2; + if (l > mid) + update(rs(pos), l, r, k); + else if (r <= mid) + update(ls(pos), l, r, k); + else + update(ls(pos), l, mid, k), update(rs(pos), mid + 1, r, k); + push_up(pos); } - int mid = (l + r) / 2; - tr[x].l = build(l, mid); - tr[x].r = build(mid + 1, r); - - return push_up(x); -} - -int update(int pos, int l, int r, int x, int p) -{ - int np = clone(p); - if (l == r) - { - tr[np].v = x; - return np; - } - int mid = (l + r) / 2; - if (pos <= mid) - tr[np].l = update(pos, l, mid, x, tr[np].l); - else - tr[np].r = update(pos, mid + 1, r, x, tr[np].r); - return push_up(np); -} +}; -ll query(int l, int r, int x, int y, int p) -{ - if (l >= x && r <= y) - return tr[p].v; - int mid = (l + r) / 2; - if (y <= mid) - return query(l, mid, x, y, tr[p].l); - else if (x > mid) - return query(mid + 1, r, x, y, tr[p].r); - return query(l, mid, x, mid, tr[p].l) + query(mid + 1, r, mid + 1, y, tr[p].r); -} ``` -## 可持久化线段树-区间第k大 +无懒标记 + ```cpp -struct pree_kth +class SegTree { - int tot = 0; - int size; - vector tr; - vector his; - map to, back; - int clone(int x) + private: + struct node { - tr[++tot] = tr[x]; - return tot; - } - - int push_up(int p) + long long l, r, v; + }; + int size = 1e6 + 5; + vector f; + inline int ls(int p) { - tr[p].v = tr[tr[p].l].v + tr[tr[p].r].v; - return p; + return p << 1; } - - pree_kth(vector &a, int n) : his(n + 1), tr(n << 5), size(n) + inline int rs(int p) { - map mp; - int cnt = 0; - for (int i = 1; i <= n; i++) - mp[a[i]]++; - for (auto [k, v] : mp) - to[k] = ++cnt, back[cnt] = k; - his[0] = build(1, n); - for (int i = 1; i <= n; i++) - his[i] = update(to[a[i]], 1, n, his[i - 1]); + return p << 1 | 1; } - int build(int l, int r) + void build(int pos, int l, int r, vector &a) { - int x = ++tot; + f[pos] = {l, r, 0}; if (l == r) { - tr[x].v = 0; - return x; + f[pos].v = a[l]; + return; } - int mid = (l + r) / 2; - tr[x].l = build(l, mid); - tr[x].r = build(mid + 1, r); - - return push_up(x); + int mid = ((l + r) >> 1); + build(ls(pos), l, mid, a); + build(rs(pos), mid + 1, r, a); + push_up(pos); + } + void push_up(int pos) + { + // modify here + f[pos].v = f[ls(pos)].v + f[rs(pos)].v; } - int update(int pos, int l, int r, int p) + public: + SegTree(long long s, vector &ori) : size(s) { - int np = clone(p); - if (l == r) + f = vector(s << 2); + build(1, 1, s, ori); + } + long long query(int pos, int l, int r) + { + if (f[pos].l >= l && f[pos].r <= r) + return f[pos].v; + int mid = (f[pos].l + f[pos].r) / 2; + if (l > mid) + return query(rs(pos), l, r); + else if (r <= mid) + return query(ls(pos), l, r); + else + // modify here + return query(ls(pos), l, mid) + query(rs(pos), mid + 1, r); + } + void update(int pos, int l, int r, long long k) + { + if (f[pos].l == l && f[pos].r == r) { - tr[np].v++; - return np; + f[pos].v += k; + return; } - int mid = (l + r) / 2; - if (pos <= mid) - tr[np].l = update(pos, l, mid, tr[np].l); + int mid = (f[pos].l + f[pos].r) / 2; + if (l > mid) + update(rs(pos), l, r, k); + else if (r <= mid) + update(ls(pos), l, r, k); else - tr[np].r = update(pos, mid + 1, r, tr[np].r); - return push_up(np); + update(ls(pos), l, mid, k), update(rs(pos), mid + 1, r, k); + push_up(pos); } +}; +``` + +zkw线段树 - int _get(int u, int v, int l, int r, int k) +```cpp +class zkw_Segtree +{ + vector tree; + int N; + void build(int n, vector &a) { - if (l == r) - return l; - int x = tr[tr[v].l].v - tr[tr[u].l].v; - int mid = (l + r) / 2; - if (x >= k) - return _get(tr[u].l, tr[v].l, l, mid, k); - else - return _get(tr[u].r, tr[v].r, mid + 1, r, k - x); + while (N <= n + 1) + N <<= 1; + tree = vector(N << 1); + for (int i = 1; i <= n; ++i) + tree[i + N] = a[i]; + for (int i = N; i; --i) + tree[i] = tree[i << 1] + tree[i << 1 | 1]; } - - int kth(int l, int r, int k) + void update(int x, int d) + { + for (int i = x + N; i; i >>= 1) + tree[i] += d; + } + int query(int l, int r) { - return back[_get(his[l - 1], his[r], 1, size, k)]; + int ans = 0; + for (l += N - 1, r += N + 1; l ^ r ^ 1; l >>= 1, r >>= 1) + { + if (~l & 1) + ans += tree[l ^ 1]; + if (r & 1) + ans += tree[r ^ 1]; + } + return ans; } }; - ``` + diff --git a/ref/jiangly_segtree.cpp b/ref/jiangly_segtree.cpp new file mode 100644 index 0000000..d968930 --- /dev/null +++ b/ref/jiangly_segtree.cpp @@ -0,0 +1,96 @@ +#include + +const int P = 998244353; +struct SegmentTree +{ + int n; + std::vector tag, sum; + SegmentTree(int n_) : n(n_), tag(4 * n, 1), sum(4 * n) + { + } + + void pull(int p) + { + sum[p] = (sum[2 * p] + sum[2 * p + 1]) % P; + } + + void mul(int p, int v) + { + tag[p] = 1LL * tag[p] * v % P; + sum[p] = 1LL * sum[p] * v % P; + } + + void push(int p) + { + mul(2 * p, tag[p]); + mul(2 * p + 1, tag[p]); + tag[p] = 1; + } + + int query(int p, int l, int r, int x, int y) + { + if (l >= y || r <= x) + { + return 0; + } + if (l >= x && r <= y) + { + return sum[p]; + } + int m = (l + r) / 2; + push(p); + return (query(2 * p, l, m, x, y) + query(2 * p + 1, m, r, x, y)) % P; + } + + int query(int x, int y) + { + return query(1, 0, n, x, y); + } + + void rangeMul(int p, int l, int r, int x, int y, int v) + { + if (l >= y || r <= x) + { + return; + } + if (l >= x && r <= y) + { + return mul(p, v); + } + int m = (l + r) / 2; + push(p); + rangeMul(2 * p, l, m, x, y, v); + rangeMul(2 * p + 1, m, r, x, y, v); + pull(p); + } + + void rangeMul(int x, int y, int v) + { + rangeMul(1, 0, n, x, y, v); + } + + void add(int p, int l, int r, int x, int v) + { + if (r - l == 1) + { + sum[p] = (sum[p] + v) % P; + return; + } + int m = (l + r) / 2; + push(p); + if (x < m) + { + add(2 * p, l, m, x, v); + } + else + { + add(2 * p + 1, m, r, x, v); + } + pull(p); + } + + void add(int x, int v) + { + add(1, 0, n, x, v); + } +}; \ No newline at end of file