Loading docs/graph/lca.md +203 −1 Original line number Diff line number Diff line Loading @@ -136,3 +136,205 @@ LCA 为两个游标跳转到同一条重链上时深度较小的那个游标所 ### 动态树 > 本节**性质**部分内容翻译自[wcipeg](http://wcipeg.com/wiki/Lowest_common_ancestor),并做过修改。 ### 标准RMQ 时间复杂度 $O(N)-O(1)$,空间复杂度 $O(N)$ ,支持在线查询,常数较大,编程复杂度较高。 流程: - 通过DFS序将树上LCA问题转为序列RMQ问题 - 通过单调栈将序列转为笛卡尔树 - 在笛卡尔树上求欧拉序,如此转化为 $\pm 1$ RMQ - 对新序列分块,做分块ST表,块内通过二进制状压DP维护 每一步的复杂度都是 $O(N)$ 的,因此总复杂度依然是 $O(N)$ 。 提供RMQ转标准RMQ的代码,为洛谷上ST表的例题[**P3865** 【模板】ST表](https://www.luogu.org/problemnew/show/P3865) ```cpp // Copyright (C) 2018 Skqliao. All rights served. #include <bits/stdc++.h> #define rep(i, l, r) for (int i = (l), _##i##_ = (r); i < _##i##_; ++i) #define rof(i, l, r) for (int i = (l) - 1, _##i##_ = (r); i >= _##i##_; --i) #define ALL(x) (x).begin(), (x).end() #define SZ(x) static_cast<int>((x).size()) typedef long long ll; typedef std::pair<int, int> pii; template<typename T> inline bool chkMin(T &a, const T &b) { return a > b ? a = b, 1 : 0; } template<typename T> inline bool chkMax(T &a, const T &b) { return a < b ? a = b, 1 : 0; } const int MAXN = 1e5 + 5; struct PlusMinusOneRMQ { const static int M = 8; int blocklen, block, Minv[MAXN], F[MAXN / M * 2 + 5][M << 1], T[MAXN], f[1 << M][M][M], S[MAXN]; void init(int n) { blocklen = std::max(1, (int)(log(n * 1.0) / log(2.0)) / 2); block = n / blocklen + (n % blocklen > 0); int total = 1 << (blocklen - 1); for (int i = 0; i < total; i++) { for (int l = 0; l < blocklen; l++) { f[i][l][l] = l; int now = 0, minv = 0; for (int r = l + 1; r < blocklen; r++) { f[i][l][r] = f[i][l][r - 1]; if ((1 << (r - 1)) & i) { now++; } else { now--; if (now < minv) { minv = now; f[i][l][r] = r; } } } } } T[1] = 0; for (int i = 2; i < MAXN; i++) { T[i] = T[i - 1]; if (!(i & (i - 1))) { T[i]++; } } } void initmin(int a[], int n) { for (int i = 0; i < n; i++) { if (i % blocklen == 0) { Minv[i / blocklen] = i; S[i / blocklen] = 0; } else { if (a[i] < a[Minv[i / blocklen]]) { Minv[i / blocklen] = i; } if (a[i] > a[i - 1]) { S[i / blocklen] |= 1 << (i % blocklen - 1); } } } for (int i = 0; i < block; i++) { F[i][0] = Minv[i]; } for (int j = 1; (1 << j) <= block; j++) { for (int i = 0; i + (1 << j) - 1 < block; i++) { int b1 = F[i][j - 1], b2 = F[i + (1 << (j - 1))][j - 1]; F[i][j] = a[b1] < a[b2] ? b1 : b2; } } } int querymin(int a[], int L, int R) { int idl = L / blocklen, idr = R / blocklen; if (idl == idr) return idl * blocklen + f[S[idl]][L % blocklen][R % blocklen]; else { int b1 = idl * blocklen + f[S[idl]][L % blocklen][blocklen - 1]; int b2 = idr * blocklen + f[S[idr]][0][R % blocklen]; int buf = a[b1] < a[b2] ? b1 : b2; int c = T[idr - idl - 1]; if (idr - idl - 1) { int b1 = F[idl + 1][c]; int b2 = F[idr - 1 - (1 << c) + 1][c]; int b = a[b1] < a[b2] ? b1 : b2; return a[buf] < a[b] ? buf : b; } return buf; } } }; struct CartesianTree { private: struct Node { int key, value, l, r; Node(int key, int value) { this->key = key; this->value = value; l = r = 0; } Node() {} }; Node tree[MAXN]; int sz; int S[MAXN], top; public: void build(int a[], int n) { top = 0; tree[0] = Node(-1, INT_MAX); S[top++] = 0; sz = 0; for (int i = 0; i < n; i++) { tree[++sz] = Node(i, a[i]); int last = 0; while (tree[S[top - 1]].value <= tree[sz].value) { last = S[top - 1]; top--; } tree[sz].l = last; tree[S[top - 1]].r = sz; S[top++] = sz; } } Node &operator [] (const int x) { return tree[x]; } }; class stdRMQ { public: void work(int a[], int n) { ct.build(a, n); dfs_clock = 0; dfs(0, 0); rmq.init(dfs_clock); rmq.initmin(depseq, dfs_clock); } int query(int L, int R) { int cl = clk[L], cr = clk[R]; if (cl > cr) { std::swap(cl, cr); } return Val[rmq.querymin(depseq, cl, cr)]; } private: CartesianTree ct; PlusMinusOneRMQ rmq; int dfs_clock, clk[MAXN], Val[MAXN << 1], depseq[MAXN << 1]; void dfs(int rt, int d) { clk[ct[rt].key] = dfs_clock; depseq[dfs_clock] = d; Val[dfs_clock++] = ct[rt].value; if (ct[rt].l) { dfs(ct[rt].l, d + 1); depseq[dfs_clock] = d; Val[dfs_clock++] = ct[rt].value; } if (ct[rt].r) { dfs(ct[rt].r, d + 1); depseq[dfs_clock] = d; Val[dfs_clock++] = ct[rt].value; } } } doit; int A[MAXN]; int main() { int n, m, l, r; scanf("%d%d", &n, &m); for (int i = 0; i < n; ++i) { scanf("%d", &A[i]); } doit.work(A, n); while (m--) { scanf("%d%d", &l, &r); printf("%d\n", doit.query(l - 1, r - 1)); } return 0; } ``` Loading
docs/graph/lca.md +203 −1 Original line number Diff line number Diff line Loading @@ -136,3 +136,205 @@ LCA 为两个游标跳转到同一条重链上时深度较小的那个游标所 ### 动态树 > 本节**性质**部分内容翻译自[wcipeg](http://wcipeg.com/wiki/Lowest_common_ancestor),并做过修改。 ### 标准RMQ 时间复杂度 $O(N)-O(1)$,空间复杂度 $O(N)$ ,支持在线查询,常数较大,编程复杂度较高。 流程: - 通过DFS序将树上LCA问题转为序列RMQ问题 - 通过单调栈将序列转为笛卡尔树 - 在笛卡尔树上求欧拉序,如此转化为 $\pm 1$ RMQ - 对新序列分块,做分块ST表,块内通过二进制状压DP维护 每一步的复杂度都是 $O(N)$ 的,因此总复杂度依然是 $O(N)$ 。 提供RMQ转标准RMQ的代码,为洛谷上ST表的例题[**P3865** 【模板】ST表](https://www.luogu.org/problemnew/show/P3865) ```cpp // Copyright (C) 2018 Skqliao. All rights served. #include <bits/stdc++.h> #define rep(i, l, r) for (int i = (l), _##i##_ = (r); i < _##i##_; ++i) #define rof(i, l, r) for (int i = (l) - 1, _##i##_ = (r); i >= _##i##_; --i) #define ALL(x) (x).begin(), (x).end() #define SZ(x) static_cast<int>((x).size()) typedef long long ll; typedef std::pair<int, int> pii; template<typename T> inline bool chkMin(T &a, const T &b) { return a > b ? a = b, 1 : 0; } template<typename T> inline bool chkMax(T &a, const T &b) { return a < b ? a = b, 1 : 0; } const int MAXN = 1e5 + 5; struct PlusMinusOneRMQ { const static int M = 8; int blocklen, block, Minv[MAXN], F[MAXN / M * 2 + 5][M << 1], T[MAXN], f[1 << M][M][M], S[MAXN]; void init(int n) { blocklen = std::max(1, (int)(log(n * 1.0) / log(2.0)) / 2); block = n / blocklen + (n % blocklen > 0); int total = 1 << (blocklen - 1); for (int i = 0; i < total; i++) { for (int l = 0; l < blocklen; l++) { f[i][l][l] = l; int now = 0, minv = 0; for (int r = l + 1; r < blocklen; r++) { f[i][l][r] = f[i][l][r - 1]; if ((1 << (r - 1)) & i) { now++; } else { now--; if (now < minv) { minv = now; f[i][l][r] = r; } } } } } T[1] = 0; for (int i = 2; i < MAXN; i++) { T[i] = T[i - 1]; if (!(i & (i - 1))) { T[i]++; } } } void initmin(int a[], int n) { for (int i = 0; i < n; i++) { if (i % blocklen == 0) { Minv[i / blocklen] = i; S[i / blocklen] = 0; } else { if (a[i] < a[Minv[i / blocklen]]) { Minv[i / blocklen] = i; } if (a[i] > a[i - 1]) { S[i / blocklen] |= 1 << (i % blocklen - 1); } } } for (int i = 0; i < block; i++) { F[i][0] = Minv[i]; } for (int j = 1; (1 << j) <= block; j++) { for (int i = 0; i + (1 << j) - 1 < block; i++) { int b1 = F[i][j - 1], b2 = F[i + (1 << (j - 1))][j - 1]; F[i][j] = a[b1] < a[b2] ? b1 : b2; } } } int querymin(int a[], int L, int R) { int idl = L / blocklen, idr = R / blocklen; if (idl == idr) return idl * blocklen + f[S[idl]][L % blocklen][R % blocklen]; else { int b1 = idl * blocklen + f[S[idl]][L % blocklen][blocklen - 1]; int b2 = idr * blocklen + f[S[idr]][0][R % blocklen]; int buf = a[b1] < a[b2] ? b1 : b2; int c = T[idr - idl - 1]; if (idr - idl - 1) { int b1 = F[idl + 1][c]; int b2 = F[idr - 1 - (1 << c) + 1][c]; int b = a[b1] < a[b2] ? b1 : b2; return a[buf] < a[b] ? buf : b; } return buf; } } }; struct CartesianTree { private: struct Node { int key, value, l, r; Node(int key, int value) { this->key = key; this->value = value; l = r = 0; } Node() {} }; Node tree[MAXN]; int sz; int S[MAXN], top; public: void build(int a[], int n) { top = 0; tree[0] = Node(-1, INT_MAX); S[top++] = 0; sz = 0; for (int i = 0; i < n; i++) { tree[++sz] = Node(i, a[i]); int last = 0; while (tree[S[top - 1]].value <= tree[sz].value) { last = S[top - 1]; top--; } tree[sz].l = last; tree[S[top - 1]].r = sz; S[top++] = sz; } } Node &operator [] (const int x) { return tree[x]; } }; class stdRMQ { public: void work(int a[], int n) { ct.build(a, n); dfs_clock = 0; dfs(0, 0); rmq.init(dfs_clock); rmq.initmin(depseq, dfs_clock); } int query(int L, int R) { int cl = clk[L], cr = clk[R]; if (cl > cr) { std::swap(cl, cr); } return Val[rmq.querymin(depseq, cl, cr)]; } private: CartesianTree ct; PlusMinusOneRMQ rmq; int dfs_clock, clk[MAXN], Val[MAXN << 1], depseq[MAXN << 1]; void dfs(int rt, int d) { clk[ct[rt].key] = dfs_clock; depseq[dfs_clock] = d; Val[dfs_clock++] = ct[rt].value; if (ct[rt].l) { dfs(ct[rt].l, d + 1); depseq[dfs_clock] = d; Val[dfs_clock++] = ct[rt].value; } if (ct[rt].r) { dfs(ct[rt].r, d + 1); depseq[dfs_clock] = d; Val[dfs_clock++] = ct[rt].value; } } } doit; int A[MAXN]; int main() { int n, m, l, r; scanf("%d%d", &n, &m); for (int i = 0; i < n; ++i) { scanf("%d", &A[i]); } doit.work(A, n); while (m--) { scanf("%d%d", &l, &r); printf("%d\n", doit.query(l - 1, r - 1)); } return 0; } ```