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;
}
posted @ 2021-06-03 09:59  siriehn_nx  阅读(767)  评论(0编辑  收藏  举报