Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 38 additions & 42 deletions src/1-ds/ds_fenwick.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,49 @@

// what: maintain prefix sums with point updates and range sum queries.
// time: build O(n), update/query O(log n); memory: O(n)
// constraint: 0-indexed; kth needs all values >= 0.
// constraint: 1-indexed [1, n]; a[0] unused; kth needs all values >= 0.
// usage: fenwick fw; fw.build(a); fw.add(p, x); fw.sum(l, r); fw.kth(k);
struct fenwick {
int n;
vector<ll> a, t;
void init(int n_) {
// goal: allocate arrays for size n.
n = n_;
a.assign(n, 0);
t.assign(n, 0);
a.assign(n + 1, 0);
t.assign(n + 1, 0);
}
void build(const vector<ll> &v) {
// goal: build fenwick in O(n) from initial array.
n = sz(v);
n = sz(v) - 1;
a = v;
t.assign(n, 0);
for (int i = 0; i < n; i++) {
t[i] += a[i];
int j = i | (i + 1);
if (j < n) t[j] += t[i];
t = a;
for (int i = 1; i <= n; i++) {
int j = i + (i & -i);
if (j <= n) t[j] += t[i];
}
}
void add(int p, ll val) {
// goal: a[p] += val.
a[p] += val;
for (int i = p; i < n; i |= i + 1) t[i] += val;
for (int i = p; i <= n; i += i & -i) t[i] += val;
}
void set(int p, ll val) { add(p, val - a[p]); }
ll sum(int x) const {
// result: prefix sum on [0..x].
// result: prefix sum on [1..x].
ll ret = 0;
for (int i = x; i >= 0; i = (i & (i + 1)) - 1) ret += t[i];
for (int i = x; i > 0; i -= i & -i) ret += t[i];
return ret;
}
ll sum(int l, int r) const { return sum(r) - (l ? sum(l - 1) : 0); }
ll sum(int l, int r) const { return sum(r) - sum(l - 1); }
int kth(ll k) const {
// result: smallest idx with prefix sum >= k.
assert(k > 0 && sum(n - 1) >= k);
int idx = -1;
assert(k > 0 && sum(n) >= k);
int idx = 0;
int bit = 1;
while (bit < n) bit <<= 1;
while (bit <= n) bit <<= 1;
for (; bit; bit >>= 1) {
int nxt = idx + bit;
if (nxt < n && t[nxt] < k) {
if (nxt <= n && t[nxt] < k) {
idx = nxt;
k -= t[nxt];
}
Expand Down Expand Up @@ -81,53 +80,50 @@ struct fenw_range { // 1-indexed

// what: 2D point updates with axis-aligned rectangle sum queries.
// time: build O(n m), update/query O(log n log m); memory: O(n m)
// constraint: 0-indexed; no bounds check.
// constraint: 1-indexed [1..n] x [1..m]; a[0][*], a[*][0] unused; no bounds check.
// usage: fenw_2d fw; fw.build(a); fw.add(x, y, v); fw.sum(x1, y1, x2, y2);
struct fenw_2d { // 0-indexed
struct fenw_2d { // 1-indexed
int n, m;
vector<vector<ll>> a, t;
void init(int n_, int m_) {
// goal: allocate arrays for n x m.
n = n_, m = m_;
a.assign(n, vector<ll>(m, 0));
t.assign(n, vector<ll>(m, 0));
a.assign(n + 1, vector<ll>(m + 1, 0));
t.assign(n + 1, vector<ll>(m + 1, 0));
}
void build(const vector<vector<ll>> &v) {
// goal: build 2D fenwick in O(n*m).
n = sz(v);
m = n ? sz(v[0]) : 0;
n = sz(v) - 1;
m = n ? sz(v[1]) - 1 : 0;
a = v;
t.assign(n, vector<ll>(m, 0));
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
t[i][j] += a[i][j];
int ni = i | (i + 1), nj = j | (j + 1);
if (ni < n) t[ni][j] += t[i][j];
if (nj < m) t[i][nj] += t[i][j];
if (ni < n && nj < m) t[ni][nj] -= t[i][j];
t = a;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) {
int nj = j + (j & -j);
if (nj <= m) t[i][nj] += t[i][j];
}
for (int j = 1; j <= m; j++)
for (int i = 1; i <= n; i++) {
int ni = i + (i & -i);
if (ni <= n) t[ni][j] += t[i][j];
}
}
}
void add(int x, int y, ll val) {
// goal: a[x][y] += val.
a[x][y] += val;
for (int i = x; i < n; i |= i + 1)
for (int j = y; j < m; j |= j + 1) t[i][j] += val;
for (int i = x; i <= n; i += i & -i)
for (int j = y; j <= m; j += j & -j) t[i][j] += val;
}
void set(int x, int y, ll val) { add(x, y, val - a[x][y]); }
ll sum(int x, int y) const {
// result: sum over rectangle [0..x] x [0..y].
// result: sum over rectangle [1..x] x [1..y].
ll ret = 0;
for (int i = x; i >= 0; i = (i & (i + 1)) - 1)
for (int j = y; j >= 0; j = (j & (j + 1)) - 1) ret += t[i][j];
for (int i = x; i > 0; i -= i & -i)
for (int j = y; j > 0; j -= j & -j) ret += t[i][j];
return ret;
}
ll sum(int x1, int y1, int x2, int y2) const {
// result: sum over rectangle [x1..x2] x [y1..y2].
ll ret = sum(x2, y2);
if (x1) ret -= sum(x1 - 1, y2);
if (y1) ret -= sum(x2, y1 - 1);
if (x1 && y1) ret += sum(x1 - 1, y1 - 1);
return ret;
return sum(x2, y2) - sum(x1 - 1, y2) - sum(x2, y1 - 1) + sum(x1 - 1, y1 - 1);
}
};
16 changes: 8 additions & 8 deletions src/1-ds/ds_merge_seg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@ constexpr int MAX_MST = 1 << 17;

// what: static range count queries by storing sorted lists on a segment tree.
// time: build O(n log n), query O(log^2 n); memory: O(n log n)
// constraint: MAX_MST >= n; values fit in int; 0-indexed [l, r]; build once.
// constraint: MAX_MST >= n; values fit in int; 1-indexed [1..n]; build once.
// usage: merge_seg st; st.build(a); st.query(l, r, k);
struct merge_seg {
vector<int> t[MAX_MST << 1];
void build(const vector<int> &a) {
// goal: build sorted lists for each node.
for (int i = 0; i < sz(a); i++)
t[i + MAX_MST].push_back(a[i]);
for (int i = 1; i < sz(a); i++)
t[i + MAX_MST - 1].push_back(a[i]);
for (int i = MAX_MST - 1; i >= 1; i--) {
t[i].resize(sz(t[i << 1]) + sz(t[i << 1 | 1]));
merge(all(t[i << 1]), all(t[i << 1 | 1]), t[i].begin());
}
}
int query(int l, int r, int k, int v = 1, int nl = 0, int nr = MAX_MST - 1) const {
int query(int l, int r, int k, int v = 1, int nl = 1, int nr = MAX_MST) const {
// result: count of elements > k in [l, r].
if (nr < l || r < nl) return 0;
if (l <= nl && nr <= r)
Expand All @@ -28,22 +28,22 @@ struct merge_seg {

// what: iterative merge sort tree for static range count queries.
// time: build O(n log n), query O(log^2 n); memory: O(n log n)
// constraint: MAX_MST >= n; values fit in int; 0-indexed [l, r]; build once.
// constraint: MAX_MST >= n; values fit in int; 1-indexed [1..n]; build once.
// usage: merge_seg_it st; st.build(a); st.query(l, r, k);
struct merge_seg_it {
vector<int> t[MAX_MST << 1];
void build(const vector<int> &a) {
// goal: build sorted lists for each node.
for (int i = 0; i < sz(a); i++)
t[i + MAX_MST].push_back(a[i]);
for (int i = 1; i < sz(a); i++)
t[i + MAX_MST - 1].push_back(a[i]);
for (int i = MAX_MST - 1; i >= 1; i--) {
t[i].resize(sz(t[i << 1]) + sz(t[i << 1 | 1]));
merge(all(t[i << 1]), all(t[i << 1 | 1]), t[i].begin());
}
}
int query(int l, int r, int k) const {
// result: count of elements > k in [l, r].
l += MAX_MST, r += MAX_MST;
l += MAX_MST - 1, r += MAX_MST - 1;
int ret = 0;
while (l <= r) {
if (l & 1) ret += int(t[l].end() - upper_bound(all(t[l]), k)), l++;
Expand Down
42 changes: 22 additions & 20 deletions src/1-ds/ds_segtree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,26 +32,26 @@ struct seg_tree {

// what: iterative segment tree for point update and range sum.
// time: build O(n), update/query O(log n); memory: O(n)
// constraint: 0-indexed [l, r).
// constraint: 1-indexed [1, n]; a[0] unused.
// usage: seg_tree_it st; st.build(a); st.set(p, v); st.query(l, r);
struct seg_tree_it { // 0-indexed
struct seg_tree_it { // 1-indexed
int n;
vector<ll> t;
void build(const vector<ll> &a) {
// goal: build tree from 0-indexed array.
n = sz(a);
// goal: build tree from 1-indexed array.
n = sz(a) - 1;
t.assign(2 * n, 0);
for (int i = 0; i < n; i++) t[n + i] = a[i];
for (int i = 1; i <= n; i++) t[n + i - 1] = a[i];
for (int i = n - 1; i >= 1; i--) t[i] = t[i << 1] + t[i << 1 | 1];
}
void set(int p, ll val) {
// goal: set a[p] = val.
for (t[p += n] = val; p > 1; p >>= 1) t[p >> 1] = t[p] + t[p ^ 1];
for (t[p += n - 1] = val; p > 1; p >>= 1) t[p >> 1] = t[p] + t[p ^ 1];
}
ll query(int l, int r) const {
// result: sum on [l, r).
// result: sum on [l, r].
ll ret = 0;
for (l += n, r += n; l < r; l >>= 1, r >>= 1) {
for (l += n - 1, r += n; l < r; l >>= 1, r >>= 1) {
if (l & 1) ret += t[l++];
if (r & 1) ret += t[--r];
}
Expand Down Expand Up @@ -183,18 +183,18 @@ struct seg_sparse {

// what: 2D point updates with rectangle sum queries on a square grid.
// time: build O(n^2), update/query O(log^2 n); memory: O(n^2)
// constraint: 0-indexed square n x n.
// constraint: 1-indexed square [1..n] x [1..n]; a[0][*], a[*][0] unused.
// usage: seg_2d st; st.build(a); st.set(x, y, v); st.query(x1, x2, y1, y2);
struct seg_2d { // 0-indexed
struct seg_2d { // 1-indexed
int n;
vector<vector<ll>> t;
void build(const vector<vector<ll>> &a) {
// goal: build 2D tree from initial grid.
n = sz(a);
n = sz(a) - 1;
t.assign(2 * n, vector<ll>(2 * n, 0));
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
t[i + n][j + n] = a[i][j];
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
t[i + n - 1][j + n - 1] = a[i][j];
for (int i = n; i < 2 * n; i++)
for (int j = n - 1; j > 0; j--)
t[i][j] = t[i][j << 1] + t[i][j << 1 | 1];
Expand All @@ -204,6 +204,7 @@ struct seg_2d { // 0-indexed
}
void set(int x, int y, ll val) {
// goal: set a[x][y] = val.
x--, y--;
t[x + n][y + n] = val;
for (int j = y + n; j > 1; j >>= 1)
t[x + n][j >> 1] = t[x + n][j] + t[x + n][j ^ 1];
Expand All @@ -222,6 +223,7 @@ struct seg_2d { // 0-indexed
}
ll query(int x1, int x2, int y1, int y2) const {
// result: sum on rectangle [x1..x2] x [y1..y2].
x1--, x2--, y1--, y2--;
ll ret = 0;
for (x1 += n, x2 += n + 1; x1 < x2; x1 >>= 1, x2 >>= 1) {
if (x1 & 1) ret += qry1d(x1++, y1, y2);
Expand All @@ -233,21 +235,21 @@ struct seg_2d { // 0-indexed

// what: 2D segment tree with coordinate compression for sparse updates/queries.
// time: prep O(q log q), update/query O(log^2 n); memory: O(q log q)
// constraint: call mark_set/mark_qry first, then prep, then set/query.
// constraint: x is 1-indexed [1..n]; y is coordinate value; call mark_set/mark_qry first, then prep, then set/query.
// usage: seg2d_comp st(n); st.mark_set(x, y); st.mark_qry(x1, x2, y1, y2); st.prep(); st.set(x, y, v); st.query(x1, x2, y1, y2);
struct seg2d_comp { // 0-indexed
struct seg2d_comp { // x: 1-indexed
int n;
vector<vector<ll>> a;
vector<vector<int>> used;
unordered_map<ll, ll> mp;
seg2d_comp(int n) : n(n), a(2 * n), used(2 * n) {}
void mark_set(int x, int y) {
// goal: record y-coordinates that will be updated.
for (x += n; x >= 1; x >>= 1) used[x].push_back(y);
for (x += n - 1; x >= 1; x >>= 1) used[x].push_back(y);
}
void mark_qry(int x1, int x2, int y1, int y2) {
// goal: record y-coordinates needed for queries.
for (x1 += n, x2 += n + 1; x1 < x2; x1 >>= 1, x2 >>= 1) {
for (x1 += n - 1, x2 += n; x1 < x2; x1 >>= 1, x2 >>= 1) {
if (x1 & 1) {
used[x1].push_back(y1);
used[x1++].push_back(y2);
Expand All @@ -274,7 +276,7 @@ struct seg2d_comp { // 0-indexed
ll k = (ll)x << 32 | (unsigned)y;
ll d = v - mp[k];
mp[k] = v;
for (x += n; x >= 1; x >>= 1) {
for (x += n - 1; x >= 1; x >>= 1) {
int i = lower_bound(all(used[x]), y) - used[x].begin() + sz(used[x]);
for (a[x][i] += d; i > 1; i >>= 1)
a[x][i >> 1] = a[x][i] + a[x][i ^ 1];
Expand All @@ -294,7 +296,7 @@ struct seg2d_comp { // 0-indexed
ll query(int x1, int x2, int y1, int y2) const {
// result: sum on rectangle [x1..x2] x [y1..y2].
ll ret = 0;
for (x1 += n, x2 += n + 1; x1 < x2; x1 >>= 1, x2 >>= 1) {
for (x1 += n - 1, x2 += n; x1 < x2; x1 >>= 1, x2 >>= 1) {
if (x1 & 1) ret += qry1d(x1++, y1, y2);
if (x2 & 1) ret += qry1d(--x2, y1, y2);
}
Expand Down
20 changes: 10 additions & 10 deletions src/4-optimizations/flow_dinic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

// what: compute maximum flow in a directed graph (Dinic).
// time: O(E V^2) worst; memory: O(E)
// constraint: 0-based; cap >= 0.
// constraint: 1-indexed [1..n]; cap >= 0.
// usage: dinic mf(n); mf.add_edge(u, v, cap); ll f = mf.max_flow(s, t);
struct dinic {
static constexpr ll INF = (1LL << 62);
Expand All @@ -22,7 +22,7 @@ struct dinic {
dinic(int n = 0) { init(n); }
void init(int n_) {
n = n_;
g.assign(n, {});
g.assign(n + 1, {});
}
edge_ref add_edge(int u, int v, ll cap) {
// goal: add forward + reverse edge
Expand All @@ -47,7 +47,7 @@ struct dinic {

bool bfs(int s, int t) {
// goal: build level graph
level.assign(n, -1);
level.assign(n + 1, -1);
queue<int> q;
level[s] = 0;
q.push(s);
Expand Down Expand Up @@ -80,7 +80,7 @@ struct dinic {
if (s == t) return 0; // edge: no flow needed
ll flow = 0;
while (flow < limit && bfs(s, t)) {
work.assign(n, 0);
work.assign(n + 1, 0);
while (flow < limit) {
ll pushed = dfs(s, t, limit - flow);
if (pushed == 0) break;
Expand All @@ -93,7 +93,7 @@ struct dinic {

// what: find feasible max flow with lower/upper bounds via transformation.
// time: dominated by dinic; memory: O(E)
// constraint: 0-based; 0 <= lo <= hi; single-use (call init(n) to reuse).
// constraint: 1-indexed [1..n]; 0 <= lo <= hi; single-use (call init(n) to reuse).
// usage: lr_dinic f(n); f.add_edge(u, v, lo, hi); auto [ok, fl] = f.max_flow(s, t);
struct lr_dinic {
static constexpr ll INF = dinic::INF;
Expand All @@ -112,7 +112,7 @@ struct lr_dinic {
void init(int n_) {
n = n_;
mf.init(n + 2);
demand.assign(n, 0);
demand.assign(n + 1, 0);
edges.clear();
}
int add_edge(int u, int v, ll lo, ll hi) {
Expand All @@ -129,8 +129,8 @@ struct lr_dinic {
ll add_demands(vector<dinic::edge_ref> &aux) {
// goal: connect ss/tt for feasible circulation
ll total = 0;
int ss = n, tt = n + 1;
for (int i = 0; i < n; i++) {
int ss = n + 1, tt = n + 2;
for (int i = 1; i <= n; i++) {
if (demand[i] > 0) {
aux.push_back(mf.add_edge(ss, i, demand[i]));
total += demand[i];
Expand All @@ -145,7 +145,7 @@ struct lr_dinic {
vector<dinic::edge_ref> aux;
aux.reserve(n);
ll total = add_demands(aux);
int ss = n, tt = n + 1;
int ss = n + 1, tt = n + 2;
ll flow = mf.max_flow(ss, tt);
for (auto ref : aux) mf.clear_edge(ref);
return flow == total;
Expand All @@ -154,7 +154,7 @@ struct lr_dinic {
if (s == t) return {feasible(), 0}; // edge: trivial s == t
vector<dinic::edge_ref> aux;
aux.reserve(n + 1);
int ss = n, tt = n + 1;
int ss = n + 1, tt = n + 2;
auto ts = mf.add_edge(t, s, INF);
ll total = add_demands(aux);
ll flow = mf.max_flow(ss, tt);
Expand Down
Loading