【CF983E】NN country

预处理出点 \(u\) 往上坐 \(2^i\) 次车后能到达的深度最浅节点 \(f_{u,i}\),查询时把两个点在深度大于 lca 时一直倍增向上跳,这时只需要查询是否有车直接经过两点。相当于这辆车的两个端点分别在这两点的子树内,用 dfs 序将其转化为二位数点即可。二位数点可以用桶排。

考虑如何预处理出 \(f\)。我们只需要在有一辆 \(u\)\(v\) 的车时更新 \(f_{u,0},f_{v,0}\),然后每个点 \(x\)\(f_{x,0}\) 就是它子树内的所有 \(f_{y,0}\) 的最小值。虽然这样会导致 \(u\rightarrow lca\rightarrow v\) 这条路径并不经过 \(lca\) 的祖先而 \(lca\) 的祖先却计算了这条路线的贡献,但是显然这个贡献对于 \(lca\) 的祖先无用——\(lca\) 不可能向下走。

复杂度 \(O(n\log n+q\log n)\)

#include <cstdio>
#include <vector>
#define gc (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 65536, stdin), p1 == p2) ? EOF : *p1 ++)

char buf[65536], *p1, *p2;
inline int read() {
	char ch;
	int x = 0;
	while ((ch = gc) < 48);
	do x = x * 10 + ch - 48; while ((ch = gc) >= 48);
	return x;
}
inline int min(const int x, const int y) {return x < y ? x : y;}
std::vector<int> G[200005];
int fa[200005][19], dep[200005], root[200005], fa2[200005][19], In[200005], Out[200005], cnt;
int ans[200005], pointcnt[200005], c[200005], len, n;
struct Rectangle {int x, y, f, id;} rect[800005];
struct Point {int x, y;} p[400005];
std::vector<Point> vec1[200005];
std::vector<Rectangle> vec2[200005];

inline void update(int x, int d) {
	for (int i = x; i <= n; i += (i & ~i + 1)) c[i] += d;
}
inline int query(int x) {
	int sum = 0;
	for (int i = x; i; i &= i - 1) sum += c[i];
	return sum;
}
void dfs(int u) {
	In[u] = ++ cnt;
	dep[u] = dep[fa[u][0]] + 1;
	for (int i = 1; i <= 18; ++ i) fa[u][i] = fa[fa[u][i - 1]][i - 1];
	for (int v : G[u]) fa[v][0] = u, dfs(v);
	Out[u] = cnt;
}
int LCA(int u, int v) {
	if (dep[u] < dep[v]) u ^= v ^= u ^= v;
	int t = dep[u] - dep[v];
	for (int i = 0; i <= 18; ++ i)
		if (t & 1 << i) u = fa[u][i];
	if (u == v) return u;
	for (int i = 18; i >= 0; -- i)
		if (fa[u][i] != fa[v][i]) u = fa[u][i], v = fa[v][i];
	return fa[u][0];
}

int main() {
	int m, q;
	n = read();
	for (int i = 2; i <= n; ++ i) G[read()].push_back(i);
	for (int i = 1; i <= n; ++ i) fa2[i][0] = i;
	dfs(1);
	m = read();
	for (int i = 1; i <= m; ++ i) {
		int u = read(), v = read();
		int lca = LCA(u, v);
		if (dep[fa2[u][0]] > dep[lca]) fa2[u][0] = lca;
		if (dep[fa2[v][0]] > dep[lca]) fa2[v][0] = lca;
		p[2 * i - 1] = {In[u], In[v]}, p[2 * i] = {In[v], In[u]};
	}
	for (int i = n; i > 1; -- i)
		if (dep[fa2[i][0]] < dep[fa2[fa[i][0]][0]]) fa2[fa[i][0]][0] = fa2[i][0];
	for (int i = 2; i <= n; ++ i)
		for (int j = 1; j <= 18; ++ j) fa2[i][j] = fa2[fa2[i][j - 1]][j - 1];
	for (int i = 1; i <= n; ++ i)
		if (fa2[i][0] == i) root[i] = i;
		else root[i] = root[fa2[i][0]];
	q = read();
	for (int T = 1; T <= q; ++ T) {
		int u = read(), v = read(), lca;
		if (root[u] != root[v]) {ans[T] = -1; continue;}
		ans[T] = 1;
		lca = LCA(u, v);
		bool flag = (u == lca) || (v == lca);
		for (int i = 18; i >= 0; -- i)
			if (dep[fa2[u][i]] > dep[lca]) u = fa2[u][i], ans[T] += 1 << i;
		for (int i = 18; i >= 0; -- i)
			if (dep[fa2[v][i]] > dep[lca]) v = fa2[v][i], ans[T] += 1 << i;
		if (!flag) {
			rect[++ len] = {In[u] - 1, In[v] - 1, 1, T};
			rect[++ len] = {In[u] - 1, Out[v], -1, T};
			rect[++ len] = {Out[u], In[v] - 1, -1, T};
			rect[++ len] = {Out[u], Out[v], 1, T};
		} else pointcnt[T] = 1;
	}
	for (int i = 1; i <= 2 * m; ++ i) vec1[p[i].x].push_back(p[i]);
	for (int i = 1, now = 0; i <= n; ++ i)
		for (int j = 0; j < vec1[i].size(); ++ j) p[++ now] = vec1[i][j];
	for (int i = 1; i <= len; ++ i) vec2[rect[i].x].push_back(rect[i]);
	for (int i = 1, now = 0; i <= n; ++ i)
	for (int j = 0; j < vec2[i].size(); ++ j) rect[++ now] = vec2[i][j];
	for (int i = 1, j = 1; i <= len; ++ i) {
		while (j <= 2 * m && p[j].x <= rect[i].x) update(p[j ++].y, 1);
		pointcnt[rect[i].id] += query(rect[i].y) * rect[i].f;
	}
	for (int i = 1; i <= q; ++ i) printf("%d\n", pointcnt[i] || ans[i] == -1 ? ans[i]: ans[i] + 1);
	return 0;
}
posted @ 2022-02-05 13:58  zqs2020  阅读(72)  评论(0编辑  收藏  举报