Repository navigation
Expand file tree
/
Copy pathsegment_tree.cpp
More file actions
204 lines (178 loc) · 7.22 KB
/
Copy pathsegment_tree.cpp
File metadata and controls
204 lines (178 loc) · 7.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
/***
*
* Segment Tree with Lazy Propagation
* Range update, range query and binary search for any associative combine and lazy update
*
* Complexity: O(n) to build, O(log n) per update, query, max_right and min_left
*
* SegmentTree<T, Ops = AddSum<T>, L = T> st(n): n copies of T(), range add and range sum by default
* SegmentTree<T, Ops, L> st(ar): built from ar
* st.update(l, r, lz): apply lz to a[l..r], 0 <= l <= r < n
* st.max_right(l, pred): largest r with pred(query(l, r)) true, l - 1 if none, 0 <= l <= n
* st.min_left(r, pred): smallest l with pred(query(l, r)) true, r + 1 if none, -1 <= r < n
* st.query(l, r): combine over a[l..r], 0 <= l <= r < n
*
* Notes:
* - pred must be monotone, once false it stays false as the range grows, and is never called on an empty range
* - Ops holds three static functions, L is the lazy type when it differs from T, and no identity is needed:
* - combine(a, b): fold of two adjacent ranges, a on the left
* - apply(val, lz, len): value of a range of len elements after lz
* - compose(old, new): one lazy meaning old then new, a lazy that changes nothing must return old
* - Arithmetic progression add (x, x + d, ... on [l, r]) with range sum: T = {sum, cnt, idx_sum}, L = {c0, c1}
* - leaves are {ar[i], 1, i}, combine and compose add componentwise, apply adds c0 * cnt + c1 * idx_sum
* - the update is update(l, r, {x - d * l, d})
* - sums reach d * n^2 / 2, so large inputs need a modulus or __int128
*
***/
#include <bits/stdtr1c++.h>
using namespace std;
template<typename T>
struct AddSum{
static T combine(T a, T b){ return a + b; }
static T apply(T val, T lz, int len){ return val + lz * len; }
static T compose(T old_lz, T new_lz){ return old_lz + new_lz; }
};
/// Inherits Ops so seg.combine(a, b) stays callable from outside, hld.cpp's query_path relies on it
template<typename T, typename Ops = AddSum<T>, typename L = T>
struct SegmentTree : Ops{
int n;
vector<T> tree;
vector<L> lazy;
vector<char> has_lazy;
SegmentTree(int n) : SegmentTree(vector<T>(n)) {}
SegmentTree(const vector<T>& ar) : n(ar.size()), tree(4 * n), lazy(4 * n), has_lazy(4 * n){
if (n) build(ar, 1, 0, n - 1);
}
void update(int l, int r, L val){
assert(0 <= l && l <= r && r < n);
update(1, 0, n - 1, l, r, val);
}
void update(int idx, int a, int b, int l, int r, const L& val){
if (a == l && b == r){
push_lazy(idx, val);
propagate(idx, a, b);
return;
}
propagate(idx, a, b);
/// the untouched child gets propagated too, so the combine below reads its true value
int p = idx << 1, q = p | 1, c = (a + b) >> 1;
if (r <= c){
propagate(q, c + 1, b);
update(p, a, c, l, r, val);
}
else if (l > c){
propagate(p, a, c);
update(q, c + 1, b, l, r, val);
}
else{
update(p, a, c, l, c, val);
update(q, c + 1, b, c + 1, r, val);
}
tree[idx] = Ops::combine(tree[p], tree[q]);
}
template<typename Pred>
int max_right(int l, Pred pred){
assert(0 <= l && l <= n);
T acc{};
bool empty = true;
int pos = l < n ? first_fail(1, 0, n - 1, l, pred, acc, empty) : -1;
return pos == -1 ? n - 1 : pos - 1;
}
template<typename Pred>
int min_left(int r, Pred pred){
assert(-1 <= r && r < n);
T acc{};
bool empty = true;
int pos = r >= 0 ? last_fail(1, 0, n - 1, r, pred, acc, empty) : -1;
return pos == -1 ? 0 : pos + 1;
}
T query(int l, int r){
assert(0 <= l && l <= r && r < n);
return query(1, 0, n - 1, l, r);
}
T query(int idx, int a, int b, int l, int r){
propagate(idx, a, b);
if (a == l && b == r) return tree[idx];
int p = idx << 1, q = p | 1, c = (a + b) >> 1;
if (r <= c) return query(p, a, c, l, r);
if (l > c) return query(q, c + 1, b, l, r);
return Ops::combine(query(p, a, c, l, c), query(q, c + 1, b, c + 1, r));
}
void build(const vector<T>& ar, int idx, int a, int b){
if (a == b){
tree[idx] = ar[a];
return;
}
int p = idx << 1, q = p | 1, c = (a + b) >> 1;
build(ar, p, a, c);
build(ar, q, c + 1, b);
tree[idx] = Ops::combine(tree[p], tree[q]);
}
void push_lazy(int idx, const L& val){
lazy[idx] = has_lazy[idx] ? Ops::compose(lazy[idx], val) : val;
has_lazy[idx] = 1;
}
void propagate(int idx, int a, int b){
if (!has_lazy[idx]) return;
tree[idx] = Ops::apply(tree[idx], lazy[idx], b - a + 1);
if (a != b){
push_lazy(idx << 1, lazy[idx]);
push_lazy(idx << 1 | 1, lazy[idx]);
}
has_lazy[idx] = 0;
}
/// First position in [max(a, l), b] where pred(acc merged with l..pos) fails, or -1, acc absorbs each passing node
template<typename Pred>
int first_fail(int idx, int a, int b, int l, Pred& pred, T& acc, bool& empty){
if (b < l) return -1;
propagate(idx, a, b);
if (l <= a){
T nxt = empty ? tree[idx] : Ops::combine(acc, tree[idx]);
if (pred(nxt)){
acc = nxt, empty = false;
return -1;
}
if (a == b) return a;
}
int c = (a + b) >> 1;
int res = first_fail(idx << 1, a, c, l, pred, acc, empty);
return res != -1 ? res : first_fail(idx << 1 | 1, c + 1, b, l, pred, acc, empty);
}
/// mirror of first_fail scanning leftwards from r, combine order kept as values pos..r for non-commutative combines
template<typename Pred>
int last_fail(int idx, int a, int b, int r, Pred& pred, T& acc, bool& empty){
if (a > r) return -1;
propagate(idx, a, b);
if (b <= r){
T nxt = empty ? tree[idx] : Ops::combine(tree[idx], acc);
if (pred(nxt)){
acc = nxt, empty = false;
return -1;
}
if (a == b) return a;
}
int c = (a + b) >> 1;
int res = last_fail(idx << 1 | 1, c + 1, b, r, pred, acc, empty);
return res != -1 ? res : last_fail(idx << 1, a, c, r, pred, acc, empty);
}
};
int main(){
/// Range add and range sum, then binary search on the sums
SegmentTree<long long> st(vector<long long>{1, 2, 3, 4, 5});
st.update(1, 3, 10); /// 1 12 13 14 5
assert(st.query(0, 4) == 45);
assert(st.query(2, 3) == 27);
assert(st.max_right(0, [](long long s){ return s <= 13; }) == 1); /// 1 + 12 fits, adding 13 does not
assert(st.min_left(4, [](long long s){ return s <= 19; }) == 3); /// 14 + 5
/// Range set and range min through an Ops struct
struct MinSet{
static int combine(int a, int b){ return min(a, b); }
static int apply(int, int lz, int){ return lz; }
static int compose(int, int lz){ return lz; }
};
SegmentTree<int, MinSet> mn(vector<int>{5, 3, 8, 6, 7});
mn.update(0, 2, 9); /// 9 9 9 6 7
assert(mn.query(0, 4) == 6);
assert(mn.query(0, 2) == 9);
return 0;
}