全局平衡 WBLT

· · 题解

没有子树和,事全局平衡二叉树很好的练习题。

首先可以维护一个函数 f_{a,o,x}(n)=((n\ \text{and}\ a)\ \text{or}\ o)\ \text{xor}\ x。可以知道这是支持复合的。所以其具有结合律,上线段树维护。

为了摊平线段树的 $\log$,可以预处理每个点的轻子树大小 $sz(u)-sz(\text{hson}(u))$。对每条重链建立线段树。线段树处理区间 $[l,r]$ 时,选取 $\text{mid}$ 为 $[l,r]$ 中轻子树大小前缀和的带权中点。 这样,树剖每跳一次,在线段树上的深度也相应减小。每次跳轻边,当次查询在线段树上的深度一定已经跳到了 $O(1)$。其实,等价于把全局平衡二叉树的二叉树换成 WBLT. 因此复杂度为 $O(n \log n)$。优势是 Leafy 且比较好写。 代码(丑见谅): ```cpp #include <bits/stdc++.h> #define ms(x, v) memset(x, v, sizeof(x)) #define il __attribute__((always_inline)) static #define U(i,l,r) for(int i(l),END##i(r);i<=END##i;++i) #define D(i,r,l) for(int i(r),END##i(l);i>=END##i;--i) using namespace std; typedef unsigned long long ll; template <class T> using BS = basic_string<T>; template <class T> void rd(T& s) { int c = getchar(); T f = 1; s = 0; while (!isdigit(c)) { if (c == '-') f = -1; c = getchar(); } while (isdigit(c)) { s = s * 10 + (c ^ 48); c = getchar(); } s *= f; } template <class T, class... Y> void rd(T& x, Y&... y) { rd(x), rd(y...); } #define meow(...) fprintf(stderr, __VA_ARGS__) const int N = 100005; const ll INF = 0xFFFFFFFFFFFFFFFF; int n, Q, K; BS<int> g[N]; int dep[N], fa[N], siz[N], hson[N]; void df1(int u, int f) { dep[u] = dep[f] + 1; fa[u] = f; siz[u] = 1; for (int v : g[u]) if (v != f) { df1(v, u); siz[u] += siz[v]; if (siz[v] > siz[hson[u]]) hson[u] = v; } } int top[N], lz[N], len[N], dfn[N]; void df2(int u, int f, int t) { static int pt; dfn[u] = ++pt; top[u] = t; len[u] = 1; if (hson[u]) df2(hson[u], u, t), len[u] = len[hson[u]] + 1; lz[dfn[u]] = siz[u] - siz[hson[u]]; for (int v : g[u]) if (v != f && v != hson[u]) df2(v, u, v); } struct T { ll a, o, x; T() : a(INF), o(0), x(0) {} T(int op, ll v) : a(INF), o(0), x(0) { if (op == 1) a = v; else if (op == 2) o = v; else x = v; } } in[N]; T operator + (T l, T r) { l.a &= r.a; l.o &= r.a; l.x &= r.a; l.o |= r.o; l.x &= ~r.o; l.x ^= r.x; return l; } struct ST { struct Node { int ls, rs, mid; T up, dn; }; vector<Node> tr; int L, R; int build(int l, int r) { assert(l <= r); int p = tr.size(), ls, rs, mid; T up, dn; tr.push_back(Node()); if (l == r) { tr[p].up = tr[p].dn = in[l]; return p; } int w = 0, h = 0; U (i, l, r) w += lz[i]; for (int i = l; h += lz[i], i <= r; ++i) if (h * 2 >= w) { mid = i; break; } if (mid == r) --mid; assert(mid >= l && mid < r); ls = build(l, mid); rs = build(mid + 1, r); up = tr[rs].up + tr[ls].up; dn = tr[ls].dn + tr[rs].dn; tr[p] = {ls, rs, mid, up, dn}; return p; } void modify(int x, T &v, int p, int l, int r) { auto &[ls, rs, mid, up, dn] = tr[p]; if (l == r) { up = dn = v; return; } if (x <= mid) modify(x, v, ls, l, mid); else modify(x, v, rs, mid + 1, r); up = tr[rs].up + tr[ls].up; dn = tr[ls].dn + tr[rs].dn; } void modify(int x, T &v) { modify(x, v, 0, L, R); } T v; void qU(int b, int e, int p, int l, int r) { auto &[ls, rs, mid, up, dn] = tr[p]; if (b <= l && e >= r) { v = v + up; return; } if (e > mid) qU(b, e, rs, mid + 1, r); if (b <= mid) qU(b, e, ls, l, mid); } T qU(int b, int e) { v = T(); qU(b, e, 0, L, R); return v; } void qD(int b, int e, int p, int l, int r) { auto &[ls, rs, mid, up, dn] = tr[p]; if (b <= l && e >= r) { v = v + dn; return; } if (b <= mid) qD(b, e, ls, l, mid); if (e > mid) qD(b, e, rs, mid + 1, r); } T qD(int b, int e) { v = T(); qD(b, e, 0, L, R); return v; } } tr[N]; T query(int u, int v) { T a, b, r; while (top[u] != top[v]) { if (dep[top[u]] > dep[top[v]]) { a = a + tr[top[u]].qU(dfn[top[u]], dfn[u]); u = fa[top[u]]; } else { b = tr[top[v]].qD(dfn[top[v]], dfn[v]) + b; v = fa[top[v]]; } } if (dep[u] < dep[v]) r = tr[top[u]].qD(dfn[u], dfn[v]); else r = tr[top[u]].qU(dfn[v], dfn[u]); return a + r + b; } ll greedy(T &t, ll m) { ll v = 0, w = 0; D (i, K - 1, 0) { #define B(s) (((s) >> (i)) & 1) ll d = (B(t.a) | B(t.o)) ^ B(t.x), e = B(t.o) ^ B(t.x); if (e) { w |= 1ull << i; } else if (d && (v | (1ull << i)) <= m) { w |= 1ull << i; v |= 1ull << i; } } return w; } int main() { rd(n, Q, K); int io[N]; ll iv[N]; U (i, 1, n) rd(io[i], iv[i]); U (i, 2, n) { int u, v; rd(u, v); g[u] += v; g[v] += u; } df1(1, 1); df2(1, 1, 1); U (i, 1, n) in[dfn[i]] = T(io[i], iv[i]); U (i, 1, n) if (i == top[i]) { tr[i].L = dfn[i], tr[i].R = dfn[i] + len[i] - 1; tr[i].tr.reserve((tr[i].R - tr[i].L) * 2 + 5); tr[i].build(tr[i].L, tr[i].R); } while (Q--) { ll op, x, y, z; rd(op, x, y, z); if (op == 1) { T t = query(x, y); ll ans = greedy(t, z); printf("%llu\n", ans); } else { T v(y, z); tr[top[x]].modify(dfn[x], v); } } } ``` ~~在本题中由于[常数较大](https://www.luogu.com.cn/record/103851897)跑不过[树剖](https://www.luogu.com.cn/record/103850937)。并且因为过大的常数,这种方法不能通过 DDP 加强版。~~