O(n)-O(1) lca
O(n)-O(1) lca
之前一直知道有这个东西,但是一直不会,网上搜了一下似乎只有 topcoder 上的英文版还有这篇博客 但是他的实现并不是很好,我看到好的博客还有$E的这篇博客。但是我并不理解他代码里的一些左移和右移明明是 \(O(n)\) 的为啥不出错?反正我是没搞清楚。而且他的这个似乎有个 \(O(2n)\) 变 \(O(n)\) 的优化,但是我的方法应该不太支持,因为就不是约束 RMQ 问题了,额,好像还有就是测了一下速度我写的应该常数比较小,实现起来和 $E 的代码的用时并没有什么区别,\(500000\) 的数据差距在 \(10\text{ms}\) 以内。
啊,来说一下思想吧。
众所周知,有一种 \(O(n\log n)-O(1)\) 求 LCA 的东西,我们可以建出来原来树的欧拉序的 ST 表,大小关系按照节点的 \(dep_i\) 或者是 \(dfn_i\) 比较均可。
然后似乎有一个非常规分块的方法就是把原序列按照每 \(\frac{\log_2 (n)}{2}\) 分成一个块,然后一共有 \(\frac{2n}{\log_2 (n)}\) 个 ,然后对于块做一个 ST 表,然后建表复杂度就是 \(O(\frac{2n}{\log n}\log (\frac{2n}{\log n}))=O(\frac{2n}{\log n}(\log (2n)-\log \log n))=O(n)\)。以下的分析称块长为 \(B\)
然后大概对于块内的查询,我们用约束 RMQ 来解决,就是一个块直接的约束唯一对应一个长度为 \(B-1\) 的零一序列。我们知道这样的序列总数是 \(2^B=O(\sqrt n)\) 的我们预处理出来每种序列的信息(其中的最小位置),这样我们的复杂度是 \(O(B\sqrt n)\) ,用 \(dfs\) 来预处理复杂度可以做到 \(O(\sqrt n)\) 但是常数明显有问题,不如上边的跑得快。
我们考虑一个查询怎么做,我们可以把不完整的两块和完整的中间的块,我们可以如果这个块最小的地方在我们查询的 \([x,y]\) 之间直接用就好,否则我们可以用位运算 \(O(1)\) 的,去把这个块 \([x,y]\) 的地方提取出来,前边补上若干个 \(0\) 即可。然后可以用预处理的信息来处理。
#include <bits/stdc++.h>
const int INF = 0x3f3f3f3f;
template<typename T>
inline T read() {
T x = 0;
char ch = getchar();
while (!isdigit(ch)) {
ch = getchar();
}
while (isdigit(ch)) {
x = x * 10 + ch - '0';
ch = getchar();
}
return x;
}
template<typename T>
void print(T x) {
if (x < 10) {
putchar(x + 48);
return;
}
print<T>(x / 10);
putchar(x % 10 + 48);
}
template<typename T>
inline T max(const T &x, const T &y) {
return x > y ? x : y;
}
template<typename T>
inline T min(const T &x, const T &y) {
return x < y ? x : y;
}
template<typename T>
inline T abs(const T &x) {
return x >= 0 ? x : -x;
}
template<typename T1, typename T2>
struct pair {
T1 first;
T2 second;
pair(const T1 &x, const T2 &y) {
first = x;
second = y;
}
friend pair operator + (const pair &x, const pair &y) {
return pair(x.first + y.first, x.second + y.second);
}
friend pair operator - (const pair &x, const pair &y) {
return pair(x.first - y.first, x.second - y.second);
}
friend bool operator < (const pair &x, const pair &y) {
return x.first < y.first || (x.first == y.first && x.second < y.second);
}
friend bool operator <= (const pair &x, const pair &y) {
return x.first < y.first || (x.first == y.first && x.second <= y.second);
}
friend bool operator > (const pair &x, const pair &y) {
return x.first > y.first || (x.first == y.first && x.second > y.second);
}
friend bool operator >= (const pair &x, const pair &y) {
return x.first > y.first || (x.first == y.first && x.second >= y.second);
}
friend bool operator == (const pair &x, const pair &y) {
return x.first == y.first && x.second == y.second;
}
friend bool operator != (const pair &x, const pair &y) {
return x.first != y.first || x.second != y.second;
}
inline pair make_pair(const T1 &x, const T2 &y) {
return pair(x, y);
}
};
const int N = 5e5 + 10;
int T, n, q, lg[N * 2], eluer[N * 2], dfc, dis[N], dfn[N], st[17][N / 4], up[128], Type[N / 4];
int cnt, h[N];
struct Edge {
int to, lac;
void insert(int x, int y) {
to = y;
lac = h[x];
h[x] = cnt++;
}
} edge[N * 2];
void dfs(int u, int fa) {
eluer[dfn[u] = ++dfc] = u;
for (int i = h[u]; ~i; i = edge[i].lac) {
if (edge[i].to != fa) {
dfs(edge[i].to, u);
eluer[++dfc] = u;
}
}
}
inline int R(int x) {
return ((x - 1) | 7) + 1;
}
inline int L(int x) {
return R(x) - 7;
}
inline int bel(int x) {
return ((x - 1) >> 3) + 1;
}
inline void Pre_Block(int x) {
int cnt = 0, mn = 0, id = 0, pos = 0;
for (int i = 6; ~i; --i) {
++pos;
cnt += ((x >> i) & 1) ? 1 : -1;
if (cnt < mn) {
cnt = mn;
id = pos;
}
}
up[x] = id;
}
inline int calcType(int id) {
int pos = 0;
for (int i = L(id); i < R(id); ++i) {
pos = (pos << 1) | (dfn[eluer[i]] < dfn[eluer[i + 1]]);
}
return pos;
}
int stMin(int x, int y) {
return dfn[x] < dfn[y] ? x : y;
}
inline int inBlockQuery(int x, int y) {
int Block_id = bel(x), u = Type[Block_id], v = L(x) + up[u];
if (x <= v && v <= y) {
return v;
}
x -= L(x);
y -= L(y);
u >>= 7 - y;
u &= ~(-1 << (y - x));
return (Block_id - 1) * 8 + up[u] - (6 - y);
}
inline int query_min(int l, int r) {
if (r < l) {
return 0;
}
int len = std::__lg(r - l + 1);
return stMin(st[len][l], st[len][r - (1 << len) + 1]);
}
inline int query(int x, int y) {
if (bel(x) == bel(y)) {
return eluer[inBlockQuery(x, y)];
}
int ans = stMin(eluer[inBlockQuery(x, R(x))], eluer[inBlockQuery(L(y), y)]);
ans = stMin(ans, query_min(bel(x) + 1, bel(y) - 1));
return ans;
}
inline int lca(int x, int y) {
return query(min(dfn[x], dfn[y]), max(dfn[x], dfn[y]));
}
int main() {
memset(h, -1, sizeof h);
dfn[0] = INF;
for (int i = 0; i < 128; ++i) {
Pre_Block(i);
}
int s;
n = read<int>();
q = read<int>();
s = read<int>();
for (int i = 1, x, y; i < n; ++i) {
x = read<int>();
y = read<int>();
edge[cnt].insert(x, y);
edge[cnt].insert(y, x);
}
dfc = 0;
dfs(s, 0);
dfc = R(dfc);
for (int i = 8; i <= dfc; i += 8) {
st[0][i / 8] = eluer[up[Type[i / 8] = calcType(i)] + i - 7];
}
for (int l = 1; l <= 16; ++l) {
int len = 1 << l;
for (int i = 1; i + len - 1 <= dfc / 8; ++i) {
st[l][i] = stMin(st[l - 1][i], st[l - 1][i + len / 2]);
}
}
for (int i = 1; i<= q; ++i) {
print<int>(lca(read<int>(), read<int>()));
putchar('\n');
}
return 0;
}