voidadd(int x, T v){ int id = x / len; for (int i = x; i < min((id + 1) * len, n); i++) prea[i] += v; for (int i = id + 1; i < min(n / len + 1, n); i++) preb[i] += v; }
vector pre(N, vector<int>(b.size() + 1)); for (int i = 1; i < N; i++) { pre[i] = pre[i - 1]; for (int j = bs[i]; j < be[i]; j++) pre[i][a[j]]++; } vector<int> cnt(b.size() + 1); vector ma(N, vector<int>(N)); for (int i = 1; i < N; i++) { cnt.assign(b.size() + 1, 0); int maxa = 0; for (int j = i; j < N; j++) { ma[i][j] = ma[i][j - 1]; for (int k = bs[j]; k < be[j]; k++) { cnt[a[k]]++; if (cnt[a[k]] == maxa) ma[i][j] = min(ma[i][j], a[k]); if (cnt[a[k]] > maxa) maxa = cnt[a[k]], ma[i][j] = a[k]; } } }
auto query = [&](int l, int r) { int res = 0; if (bk[l] == bk[r - 1]) { for (int i = l; i < r; i++) cnt[a[i]] = 0; int maxa = 0; for (int i = l; i < r; i++) { cnt[a[i]]++; if (maxa == cnt[a[i]]) res = min(res, a[i]); if (maxa < cnt[a[i]]) maxa = cnt[a[i]], res = a[i]; } } else { int L = bk[l], R = bk[r - 1]; res = ma[L + 1][R - 1]; for (int i = l; i < be[L]; i++) cnt[a[i]] = pre[R - 1][a[i]] - pre[L][a[i]]; for (int i = bs[R]; i < r; i++) cnt[a[i]] = pre[R - 1][a[i]] - pre[L][a[i]]; int maxa = pre[R - 1][res] - pre[L][res]; for (int i = l; i < be[L]; i++) { cnt[a[i]]++; if (maxa == cnt[a[i]]) res = min(res, a[i]); if (maxa < cnt[a[i]]) maxa = cnt[a[i]], res = a[i]; } for (int i = bs[R]; i < r; i++) { cnt[a[i]]++; if (maxa == cnt[a[i]]) res = min(res, a[i]); if (maxa < cnt[a[i]]) maxa = cnt[a[i]], res = a[i]; } } return res; };
树分块 / 王室联邦分块
王室联邦分块是最经典、最常用的一种树分块方法,其构建的分块具有如下性质:
块大小有界:每个块的大小在 [B, 2B] 之间。保证了块的规模大致均匀。
省会的性质:每个块均有一个块外的省会,省会节点比块中所有节点深度都小。
块的连通性:每个块加上其省会是原树的一个连通块,但可能有多个块共享省会。
通过这种分块,可以利用省会建立虚树在线地解决问题,或利用树上莫队算法离线查询问题。
以下实现中,key 为每个块的省会,kid
为每个节点作为省会对应的任意块 id,非省会则为 0 ,bk 为每个节点所属块 id
,kfa 为省会间建立的虚树父亲关系。
voidadd(int x, T v){ for (int i = x; i < a.size(); i += i & -i) a[i] += v; }
T sum(int x){ auto ans = T(); for (int i = x; i; i -= i & -i) ans += a[i]; return ans; } };
树状数组上二分
返回最后一个使前缀和小于指定值 k 的下标 x 的下一位,即权值树状数组第 k 小。
1 2 3 4 5 6 7 8 9 10
intkth(T k){ int x = 0; auto cur = T(); for (int i = bit_floor(a.size()); i; i >>= 1) { if (x + i >= a.size()) continue; if (cur + a[x + i] >= k) continue; x += i, cur += a[x]; } return x + 1; }
template<typename Info, typename Tag> structSegmentTree { vector<Info> seg; vector<Tag> tag; explicitSegmentTree(const vector<Info> &val) : seg(val.size() * 4), tag(val.size() * 4) { auto build = [&](auto &&self, int u, int l, int r) { if (r - l == 1) { seg[u] = val[l]; return; } int m = l + r >> 1; self(self, u << 1, l, m); self(self, u << 1 | 1, m, r); seg[u] = Info::merge(seg[u << 1], seg[u << 1 | 1]); }; build(build, 1, 0, val.size()); } voidpush(int u, int l, int r){ int m = l + r >> 1; seg[u << 1].apply(tag[u], l, m); tag[u << 1].apply(tag[u], l, m); seg[u << 1 | 1].apply(tag[u], m, r); tag[u << 1 | 1].apply(tag[u], m, r); tag[u] = Tag(); } voidapply(int u, int l, int r, int ql, int qr, const Tag &t){ if (l >= qr || r <= ql) return; if (l >= ql && r <= qr) { seg[u].apply(t, l, r); tag[u].apply(t, l, r); return; } push(u, l, r); int m = l + r >> 1; apply(u << 1, l, m, ql, qr, t); apply(u << 1 | 1, m, r, ql, qr, t); seg[u] = Info::merge(seg[u << 1], seg[u << 1 | 1]); } Info query(int u, int l, int r, int ql, int qr){ if (l >= qr || r <= ql) returnInfo(); if (l >= ql && r <= qr) return seg[u]; push(u, l, r); int m = l + r >> 1; return Info::merge(query(u << 1, l, m, ql, qr), query(u << 1 | 1, m, r, ql, qr)); } };
template<typename Info, typename Tag> structSegmentTree { vector<Info> seg; vector<Tag> tag; vector<int> lch, rch; explicitSegmentTree() : seg(1), tag(1), lch(1), rch(1) { } intcreate(){ int x = seg.size(); seg.emplace_back(); tag.emplace_back(); lch.push_back(0); rch.push_back(0); return x; } voidpush(int u, int l, int r){ int m = l + r >> 1; seg[lch[u]].apply(tag[u], l, m); tag[lch[u]].apply(tag[u], l, m); seg[rch[u]].apply(tag[u], m, r); tag[rch[u]].apply(tag[u], m, r); tag[u] = Tag(); } voidapply(int u, int l, int r, int ql, int qr, const Tag &t){ if (l >= qr || r <= ql) return; if (l >= ql && r <= qr) { seg[u].apply(t, l, r); tag[u].apply(t, l, r); return; } push(u, l, r); int m = l + r >> 1; if (!lch[u]) lch[u] = create(); if (!rch[u]) rch[u] = create(); apply(lch[u], l, m, ql, qr, t); apply(rch[u], m, r, ql, qr, t); seg[u] = Info::merge(seg[lch[u]], seg[rch[u]]); } Info query(int u, int l, int r, int ql, int qr){ if (!u || l >= qr || r <= ql) returnInfo(); if (l >= ql && r <= qr) return seg[u]; push(u, l, r); int m = l + r >> 1; return Info::merge(query(lch[u], l, m, ql, qr), query(rch[u], m, r, ql, qr)); } };
structLine { f80 k, b; int id; f80 f(i64 x)const{ return k * x + b; } staticboolbetter(const Line &a, const Line &b, i64 x){ f80 ya = a.f(x), yb = b.f(x); if (fabs(ya - yb) < eps) return a.id < b.id; return ya > yb; } }; template<typename Line> structLiChaoTree { vector<Line> seg; vector<int> lch, rch; LiChaoTree() : seg(1), lch(1), rch(1) { } intcreate(){ int x = seg.size(); seg.emplace_back(); lch.push_back(0); rch.push_back(0); return x; } voidupdate(int u, i64 l, i64 r, Line val){ if (seg[u].id == 0) { seg[u] = val; return; } i64 m = l + r >> 1; if (Line::better(val, seg[u], m)) swap(val, seg[u]); if (r - l == 1) return; if (Line::better(val, seg[u], l)) { if (!lch[u]) lch[u] = create(); update(lch[u], l, m, val); } elseif (Line::better(val, seg[u], r)) { if (!rch[u]) rch[u] = create(); update(rch[u], m, r, val); } } voidinsert(int u, i64 l, i64 r, i64 ql, i64 qr, const Line &val){ if (ql >= r || qr <= l) return; if (ql <= l && r <= qr) { update(u, l, r, val); return; } i64 m = l + r >> 1; if (ql < m) { if (!lch[u]) lch[u] = create(); insert(lch[u], l, m, ql, qr, val); } if (qr > m) { if (!rch[u]) rch[u] = create(); insert(rch[u], m, r, ql, qr, val); } } Line query(int u, i64 l, i64 r, i64 x){ if (!u) return Line{}; Line res = seg[u]; if (l == r) return res; i64 m = l + r >> 1; Line nres{}; if (x < m) nres = query(lch[u], l, m, x); else nres = query(rch[u], m, r, x); if (!res.id) return nres; if (!nres.id) return res; return Line::better(res, nres, x) ? res : nres; } };
template<typename T> structSplay { int rt = 0; vector<T> val; vector<array<int, 2> > ch; vector<int> fa, sz, cnt; Splay() : val(1), ch(1), fa(1), sz(1), cnt(1) { rt = 0; } voidcreate(int f, T v){ val.push_back(v); ch.emplace_back(); fa.push_back(f); sz.push_back(1); cnt.push_back(1); } intget(int x)const{ return x == ch[fa[x]][1]; } voidpull(int x){ sz[x] = sz[ch[x][0]] + sz[ch[x][1]] + cnt[x]; } voidrotate(int x){ int y = fa[x], z = fa[y], dir = get(x); if (z) ch[z][get(y)] = x; if (ch[x][dir ^ 1]) fa[ch[x][dir ^ 1]] = y; fa[y] = x, fa[x] = z; ch[y][dir] = ch[x][dir ^ 1], ch[x][dir ^ 1] = y; pull(y), pull(x); } voidsplay(int x){ for (int f; (f = fa[x]); rotate(x)) { if (fa[f]) rotate(get(x) == get(f) ? f : x); } rt = x; } voidinsert(T v){ int tot = val.size(); if (!rt) { create(rt, v), rt = tot; return; } int x = rt, f = 0; while (x) { f = x; if (val[x] == v) break; int dir = v > val[x]; x = ch[x][dir]; } if (!x && f) create(f, v), ch[f][val[f] < v] = tot, splay(tot); if (x) cnt[x]++, pull(x), splay(x); } intfind(T v){ int x = rt, f = 0; while (x) { f = x; if (val[x] == v) break; int dir = v > val[x]; x = ch[x][dir]; } if (!x && f) splay(f); if (x) splay(x); return x; } voidremove(T v){ int x = find(v); if (!x) return; if (cnt[x] > 1) { --cnt[x]; pull(x); return; } int l = ch[x][0], r = ch[x][1]; ch[x][0] = ch[x][1] = fa[x] = 0; if (!l && !r) { rt = 0; } elseif (!l) { rt = r, fa[r] = 0; } elseif (!r) { rt = l, fa[l] = 0; } else { int pre = l; while (ch[pre][1]) pre = ch[pre][1]; splay(pre); ch[pre][1] = r; fa[r] = pre; pull(pre); rt = pre; } } intrank(T v){ int x = rt, res = 0; while (x) { if (v < val[x]) { x = ch[x][0]; } else { res += sz[ch[x][0]]; if (v == val[x]) break; res += cnt[x]; x = ch[x][1]; } } if (x) splay(x); return res + 1; } T kth(int k){ int x = rt; while (x) { int lsz = sz[ch[x][0]]; if (k <= lsz) x = ch[x][0]; elseif (k <= lsz + cnt[x]) break; else k -= lsz + cnt[x], x = ch[x][1]; } if (x) splay(x); return val[x]; } T prev(T v){ int x = rt, res = 0; while (x) { if (val[x] < v) res = x, x = ch[x][1]; else x = ch[x][0]; } if (res) splay(res); return val[res]; } T next(T v){ int x = rt, res = 0; while (x) { if (val[x] > v) res = x, x = ch[x][0]; else x = ch[x][1]; } if (res) splay(res); return val[res]; } };
动态树
Link-Cut Tree
Link-Cut Tree
是一种用于高效维护动态森林(支持树的连接、分割)和路径查询的数据结构,其核心操作(连接、分割、路径查询等)的均摊时间复杂度为
O(log n),广泛应用于动态图算法和网络流问题。
template<typename Info> structPersistentSegmentTree { vector<Info> seg; vector<int> lch, rch; explicitPersistentSegmentTree() : seg(1), lch(1), rch(1) { } intbuild(const vector<Info> &val){ auto build = [&](auto &&self, int u, int l, int r) { if (r - l == 1) { seg[u] = val[l]; return; } int m = l + r >> 1; self(self, lch[u] = create(), l, m); self(self, rch[u] = create(), m, r); seg[u] = Info::merge(seg[lch[u]], seg[rch[u]]); }; int rt = create(); build(build, rt, 1, val.size()); return rt; } intcreate(){ int x = seg.size(); seg.emplace_back(); lch.push_back(0); rch.push_back(0); return x; } intbranch(int u){ int v = create(); seg[v] = seg[u]; lch[v] = lch[u]; rch[v] = rch[u]; return v; } voidmodify(int u, int v, int l, int r, int x, Info k){ if (r - l == 1) { seg[v] = k; return; } int m = l + r >> 1; if (x < m) { int lu = lch[u]; rch[v] = rch[u]; modify(lu, lch[v] = create(), l, m, x, k); } else { int ru = rch[u]; lch[v] = lch[u]; modify(ru, rch[v] = create(), m, r, x, k); } seg[v] = Info::merge(seg[lch[v]], seg[rch[v]]); } Info query(int u, int l, int r, int ql, int qr){ if (!u || l >= qr || r <= ql) returnInfo(); if (l >= ql && r <= qr) return seg[u]; int m = l + r >> 1; return Info::merge(query(lch[u], l, m, ql, qr), query(rch[u], m, r, ql, qr)); } };
可持久化权值线段树 / 主席树
主席树是一种线段树差分的思想,基于可持久化权值线段树实现的主席树可以实现查询区间第
k
小的功能,修改和查询操作的时空复杂度均为 Θ(log n) ,其中 n 是值域大小。
template<typename T> structPersistentSegmentTree { vector<T> seg; vector<int> lch, rch; explicitPersistentSegmentTree() : seg(1), lch(1), rch(1) { } intcreate(){ int x = seg.size(); seg.emplace_back(); lch.push_back(0); rch.push_back(0); return x; } intbranch(int u){ int v = create(); seg[v] = seg[u]; lch[v] = lch[u]; rch[v] = rch[u]; return v; } voidmodify(int u, int v, int l, int r, int x, T k){ if (r - l == 1) { seg[v] = seg[u] + k; return; } int m = l + r >> 1; if (x < m) { int lu = lch[u]; rch[v] = rch[u]; modify(lu, lch[v] = create(), l, m, x, k); } else { int ru = rch[u]; lch[v] = lch[u]; modify(ru, rch[v] = create(), m, r, x, k); } seg[v] = seg[lch[v]] + seg[rch[v]]; } intkth(int u, int v, int l, int r, int k){ int m = l + r >> 1; if (r - l == 1) return l; int x = seg[lch[v]] - seg[lch[u]]; if (k <= x) returnkth(lch[u], lch[v], l, m, k); returnkth(rch[u], rch[v], m, r, k - x); } };
标记永久化 / 区间修改
使用标记永久化的思想实现可区间修改的可持久化线段树及其树上二分查询第
k
小,修改和查询操作的时空复杂度均为 Θ(log n) ,其中 n 是值域大小。
template<typename T> structPersistentSegmentTree { vector<T> seg, tag; vector<int> lch, rch; explicitPersistentSegmentTree() : seg(1), tag(1), lch(1), rch(1) { } intcreate(){ int x = seg.size(); seg.emplace_back(); tag.emplace_back(); lch.push_back(0); rch.push_back(0); return x; } intbranch(int u){ int v = create(); seg[v] = seg[u]; lch[v] = lch[u]; rch[v] = rch[u]; tag[v] = tag[u]; return v; } voidapply(int u, int v, int l, int r, int ql, int qr, T k){ if (ql >= r || qr <= l) return; if (ql <= l && r <= qr) { tag[v] = tag[u] + k; seg[v] = seg[u] + k * (r - l); return; } int m = l + r >> 1; int lu = lch[u], ru = rch[u]; if (ql < m) apply(lu, lch[v] = create(), l, m, ql, qr, k); else lch[v] = lu; if (qr > m) apply(ru, rch[v] = create(), m, r, ql, qr, k); else rch[v] = ru; seg[v] = seg[lch[v]] + seg[rch[v]] + tag[v] * (r - l); } T query(int u, int v, int l, int r, int ql, int qr, T t){ if (ql >= r || qr <= l) returnT(); if (ql <= l && r <= qr) return seg[v] - seg[u] + t * (r - l); int m = l + r >> 1; returnquery(lch[u], lch[v], l, m, ql, qr, t + tag[v] - tag[u]) + query(rch[u], rch[v], m, r, ql, qr, t + tag[v] - tag[u]); } intkth(int u, int v, int l, int r, int k, T t){ if (r - l == 1) return l; int m = l + r >> 1; T x = seg[lch[v]] - seg[lch[u]] + (t + tag[v] - tag[u]) * (m - l); if (k <= x) returnkth(lch[u], lch[v], l, m, k, t + tag[v] - tag[u]); returnkth(rch[u], rch[v], m, r, k - x, t + tag[v] - tag[u]); } };
template<typename T> structPersistentArray { vector<T> seg; vector<int> lch, rch; explicitPersistentArray() : seg(1), lch(1), rch(1) { } intcreate(){ int x = seg.size(); seg.emplace_back(); lch.push_back(0); rch.push_back(0); return x; } intbranch(int u){ int v = create(); seg[v] = seg[u]; lch[v] = lch[u]; rch[v] = rch[u]; return v; } voidmodify(int u, int v, int l, int r, int x, T k){ if (r - l == 1) { seg[v] = k; return; } int m = l + r >> 1; if (x < m) { int lu = lch[u]; rch[v] = rch[u]; modify(lu, lch[v] = create(), l, m, x, k); } else { int ru = rch[u]; lch[v] = lch[u]; modify(ru, rch[v] = create(), m, r, x, k); } } T query(int u, int l, int r, int x){ if (r - l == 1) return seg[u]; int m = l + r >> 1; return x < m ? query(lch[u], l, m, x) : query(rch[u], m, r, x); } };
structPDSU { int n; vector<int> rt; PersistentArray<int> p; explicitPDSU(int n): rt(1) { this->n = n; for (int i = 0; i < n; i++) { p.modify(0, 0, 0, n, i, -1); } } intbranch(int u){ int v = rt.size(); rt.push_back(p.branch(rt[u])); return v; } intfind(int u, int x){ int f = p.query(rt[u], 0, n, x); if (f < 0) return x; returnfind(u, f); } boolsame(int u, int x, int y){ returnfind(u, x) == find(u, y); } boolmerge(int u, int x, int y){ x = find(u, x), y = find(u, y); if (x == y) returnfalse; int sx = p.query(rt[u], 0, n, x); int sy = p.query(rt[u], 0, n, y); if (sx > sy) swap(x, y); p.modify(rt[u], rt[u], 0, n, x, sx + sy); p.modify(rt[u], rt[u], 0, n, y, x); returntrue; } };
structPersistent01Trie { staticconstexprint maxb = 24; vector<array<int, 2> > ch; vector<int> pre; Persistent01Trie() : ch(1), pre(1) { } intcreate(){ int x = ch.size(); ch.emplace_back(); pre.push_back(0); return x; } intbranch(int u){ int v = create(); ch[v] = ch[u]; pre[v] = pre[u]; return v; } voidinsert(int u, int v, int x){ pre[v] = pre[u] + 1; for (int i = maxb; i >= 0; i--) { int b = x >> i & 1; int cu = ch[u][b]; ch[v][b] = create(); ch[v][!b] = ch[u][!b]; pre[ch[v][b]] = pre[cu] + 1; u = cu, v = ch[v][b]; } } intquery(int u, int v, int x)const{ int res = 0; for (int i = maxb; i >= 0; i--) { int b = x >> i & 1; if (pre[ch[v][b ^ 1]] - pre[ch[u][b ^ 1]]) { u = ch[u][b ^ 1], v = ch[v][b ^ 1], res |= 1 << i; } else { u = ch[u][b], v = ch[v][b]; } } return res; } };
Fenwick<int> d(n + 1, m + 1), di(n + 1, m + 1), dj(n + 1, m + 1), dij(n + 1, m + 1);
auto apply = [&](int x1, int y1, int x2, int y2, int v) { auto set = [&](int x, int y, int v) { d.add(x, y, v); di.add(x, y, v * x); dj.add(x, y, v * y); dij.add(x, y, v * x * y); }; set(x1, y1, v); set(x1, y2 + 1, -v); set(x2 + 1, y1, -v); set(x2 + 1, y2 + 1, v); };
auto query = [&](int x1, int y1, int x2, int y2) { auto get = [&](int x, int y) { return d.sum(x, y) * (x + 1) * (y + 1) - di.sum(x, y) * (y + 1) - dj.sum(x, y) * (x + 1) + dij.sum(x, y); }; returnget(x2, y2) - get(x2, y1 - 1) - get(x1 - 1, y2) + get(x1 - 1, y1 - 1); };
structSegmentTree { vector<int> seg, lch, rch; explicitSegmentTree() : seg(1), lch(1), rch(1) { } intcreate(){ int y = seg.size(); seg.emplace_back(); lch.push_back(0); rch.push_back(0); return y; } voidmodify(int u, int l, int r, int x, int k){ if (r - l == 1) { seg[u] += k; return; } int m = l + r >> 1; if (x < m) { if (!lch[u]) lch[u] = create(); modify(lch[u], l, m, x, k); } else { if (!rch[u]) rch[u] = create(); modify(rch[u], m, r, x, k); } seg[u] = seg[lch[u]] + seg[rch[u]]; } intquery(int u, int l, int r, int ql, int qr){ if (!u || l >= qr || r <= ql) return0; if (l >= ql && r <= qr) return seg[u]; int m = l + r >> 1; returnquery(lch[u], l, m, ql, qr) + query(rch[u], m, r, ql, qr); } }; structNestedSegmentTree { vector<int> seg, lch, rch; SegmentTree segy; explicitNestedSegmentTree() : seg(1), lch(1), rch(1) { } intcreate(){ int y = segy.create(); int x = seg.size(); seg.push_back(y); lch.push_back(0); rch.push_back(0); return x; } voidmodify(int u, int lx, int rx, int ly, int ry, int x, int y, int k){ segy.modify(seg[u], ly, ry, y, k); if (rx - lx == 1) return; int mx = lx + rx >> 1; if (x < mx) { if (!lch[u]) lch[u] = create(); modify(lch[u], lx, mx, ly, ry, x, y, k); } else { if (!rch[u]) rch[u] = create(); modify(rch[u], mx, rx, ly, ry, x, y, k); } } intquery(int u, int lx, int rx, int ly, int ry, int qxl, int qxr, int qyl, int qyr){ if (!u || lx >= qxr || rx <= qxl) return0; if (lx >= qxl && rx <= qxr) return segy.query(seg[u], ly, ry, qyl, qyr); int mx = lx + rx >> 1; returnquery(lch[u], lx, mx, ly, ry, qxl, qxr, qyl, qyr) + query(rch[u], mx, rx, ly, ry, qxl, qxr, qyl, qyr); } };
structPersistentSegmentTree { vector<int> seg; vector<int> lch, rch; explicitPersistentSegmentTree() : seg(1), lch(1), rch(1) { } intcreate(){ int y = (int) seg.size(); seg.emplace_back(); lch.push_back(0); rch.push_back(0); return y; } voidmodify(int u, int l, int r, int y, int k){ if (r - l == 1) { seg[u] += k; return; } int m = l + r >> 1; if (y < m) { if (!lch[u]) lch[u] = create(); modify(lch[u], l, m, y, k); } else { if (!rch[u]) rch[u] = create(); modify(rch[u], m, r, y, k); } seg[u] = seg[lch[u]] + seg[rch[u]]; } intquery(vector<int> &u, vector<int> &v, int l, int r, int k){ if (r - l == 1) return l; int x = 0; for (auto i: u) x -= seg[lch[i]]; for (auto i: v) x += seg[lch[i]]; int m = l + r >> 1; if (k <= x) { for (auto &i: u) i = lch[i]; for (auto &i: v) i = lch[i]; returnquery(u, v, l, m, k); } else { for (auto &i: u) i = rch[i]; for (auto &i: v) i = rch[i]; returnquery(u, v, m, r, k - x); } } }; structFenwickNestedSegmentTree { vector<int> a; PersistentSegmentTree segy; explicitFenwickNestedSegmentTree(int n) : a(n) { for (int i = 1; i < n; i++) a[i] = segy.create(); } voidmodify(int x, int l, int r, int y, int k){ for (int i = x; i < a.size(); i += i & -i) { segy.modify(a[i], l, r, y, k); } } intkth(int l, int r, int lv, int rv, int v){ vector<int> rtu, rtv; for (int i = l; i; i -= i & -i) rtu.push_back(a[i]); for (int i = r; i; i -= i & -i) rtv.push_back(a[i]); return segy.query(rtu, rtv, lv, rv, v); } };
structBitRank { vector<u64> B; vector<int> cnt; explicitBitRank(int n) : B(((n + 1) >> 6) + 1), cnt(B.size()) { } voidset(int i, u64 val){ B[i >> 6] |= val << (i & 63); } voidbuild(){ for (int i = 1; i < B.size(); i++) { cnt[i] = cnt[i - 1] + __builtin_popcountll(B[i - 1]); } } intrank1(int i){ return cnt[i >> 6] + __builtin_popcountll(B[i >> 6] & (1ull << (i & 63)) - 1); } intrank0(int i){ return i - rank1(i); } }; structWaveletMatrix { int ht; vector<int> pos; vector<BitRank> bk; explicitWaveletMatrix(vector<int> vec){ int sig = *max_element(vec.begin(), vec.end()); ht = sig == 0 ? 1 : 64 - __builtin_clzll(sig); pos.resize(ht), bk.resize(ht, BitRank(vec.size())); for (int i = 0; i < ht; i++) { for (int j = 0; j < vec.size(); j++) { bk[i].set(j, vec[j] >> (ht - i - 1) & 1); } bk[i].build(); auto it = stable_partition(vec.begin(), vec.end(), [&](int c) { return ~c >> (ht - i - 1) & 1; }); pos[i] = it - vec.begin(); } } intrank(int i, int v){ int p = 0; for (int j = 0; j < ht; j++) { if (v >> (ht - j - 1) & 1) { p = pos[j] + bk[j].rank1(p); i = pos[j] + bk[j].rank1(i); } else { p = bk[j].rank0(p); i = bk[j].rank0(i); } } return i - p; } intrank(int l, int r, int v){ returnrank(r, v) - rank(l, v); } intkth(int l, int r, int k){ int res = 0; for (int i = 0; i < ht; i++) { int j = bk[i].rank0(r) - bk[i].rank0(l); if (j > k) { l = bk[i].rank0(l); r = bk[i].rank0(r); } else { l = pos[i] + bk[i].rank1(l); r = pos[i] + bk[i].rank1(r); k -= j; res |= 1 << (ht - i - 1); } } return res; } intrangefreq(int i, int j, int a, int b, int l, int r, int x){ if (i == j || r <= a || b <= l) return0; int m = (l + r) >> 1; if (a <= l && r <= b) return j - i; int left = rangefreq(bk[x].rank0(i), bk[x].rank0(j), a, b, l, m, x + 1); int right = rangefreq(pos[x] + bk[x].rank1(i), pos[x] + bk[x].rank1(j), a, b, m, r, x + 1); return left + right; } intrangefreq(int l, int r, int a, int b){ returnrangefreq(l, r, a, b, 0, 1 << ht, 0); } intrangemin(int i, int j, int a, int b, int l, int r, int x, int v){ if (i == j || r <= a || b <= l) return-1; if (r - l == 1) return v; int m = (l + r) >> 1; int res = rangemin(bk[x].rank0(i), bk[x].rank0(j), a, b, l, m, x + 1, v); if (res >= 0) return res; returnrangemin(pos[x] + bk[x].rank1(i), pos[x] + bk[x].rank1(j), a, b, m, r, x + 1, v + (1 << (ht - x - 1))); } intrangemin(int l, int r, int a, int b){ returnrangemin(l, r, a, b, 0, 1 << ht, 0, 0); } };