【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;
inline bool operator < (const Rectangle a) const {return x < a.x;}
} rect[800005];
struct Point {
int x, y;
inline bool operator < (const Point a) const {return x < a.x;}
} 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;
}
标签:dep,200005,NN,fa,int,country,fa2,lca,CF983E 来源: https://www.cnblogs.com/stinger/p/15864164.html