CF 983E NN country
题意
给定一棵 \(n\) 个点的树和 \(m\) 条树上路径,\(q\) 次询问 \(x\to y\) 最少用多少条路径。
\(n, m, q\leq 2\times 10 ^5\)。
题解
有一个贪心策略:我们考虑同时操作 \(x\) 和 \(y\),每次让它们跳到能跳到的最浅的点。直到跳到再往上跳就超过 \(lca\) 了为止。此时如果有一条路径同时经过了 \(x'\) 和 \(y'\),那么只需要再跳一次,否则要 \(x'\) 和 \(y'\) 需要再同时往上跳一步。
我们搞一个倍增数组 \(jmp[u][i]\) 表示 \(u\) 向上跳 \(2^i\) 次能跳到的最浅的点。
考虑判断是否有路径经过 \(x\) 和 \(y\)。离线所有询问。我们在 \(dfs\) 到 \(x\) 的时候,如果有以 \(x\) 为起点的路径,就给对应的终点打标记。如果在进入 \(x\) 子树的时候和回溯的时候 \(y\) 子树内的标记数不一样,说明存在路径。可以使用树状数组维护。
注意额外判无解,\(x\) 或 \(y\) 为 \(lca\) 的情况。
#include <bits/stdc++.h>
const int N = 2e5 + 10;
#define pii std::pair<int, int>
#define mkp(a, b) std::make_pair(a, b)
inline int read()
{
int x = 0;
char ch = getchar();
while (!isdigit(ch)) ch = getchar();
while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
return x;
}
struct Bit
{
int f[N];
#define lowbit(x) (x & -x)
inline void Add(int x)
{
for (; x < N; x += lowbit(x)) f[x]++;
}
inline int Ask(int x)
{
int res = 0;
for (; x; x -= lowbit(x)) res += f[x];
return res;
}
inline int Ask(int l, int r)
{
return Ask(r) - Ask(l - 1);
}
}T;
std::vector<pii> V[N];
std::vector<int> G[N], F[N];
int tot = 1, fir[N], nex[N << 1], got[N << 1], ans[N], res[N], flg[N];
int idx = 1, dep[N], siz[N], dfn[N], rev[N], par[N][21], jmp[N][21];
inline int cmp(int a, int b) {return dep[a] < dep[b] ? a : b;}
inline void AddEdge(int u, int v)
{
nex[++tot] = fir[u], fir[u] = tot, got[tot] = v;
nex[++tot] = fir[v], fir[v] = tot, got[tot] = u;
}
inline void dfs(int u, int fa)
{
dep[u] = dep[par[u][0] = fa] + 1, siz[u] = 1, dfn[u] = ++idx, rev[idx] = u;
for (int i = 1; i <= 20; ++i) par[u][i] = par[par[u][i - 1]][i - 1];
for (int i = fir[u]; i; i = nex[i]) if (got[i] != fa) dfs(got[i], u), siz[u] += siz[got[i]];
}
inline int getlca(int u, int v)
{
if (dep[u] < dep[v]) std::swap(u, v);
for (int i = 20; i >= 0; --i) if (dep[par[u][i]] >= dep[v]) u = par[u][i];
if (u == v) return u;
for (int i = 20; i >= 0; --i) if (par[u][i] != par[v][i]) u = par[u][i], v = par[v][i];
return par[u][0];
}
inline void Dfs(int u, int fa)
{
jmp[u][0] = u;
for (auto v: G[u]) jmp[u][0] = cmp(jmp[u][0], getlca(u, v));
for (int i = fir[u]; i; i = nex[i]) if (got[i] != fa)
Dfs(got[i], u), jmp[u][0] = cmp(jmp[u][0], jmp[got[i]][0]);
}
inline int jump(int &u, int lca)
{
int res = 0;
for (int i = 20; i >= 0; --i)
if (dep[jmp[u][i]] > dep[lca] && (i || jmp[u][i] != jmp[u][i - 1])) u = jmp[u][i], res = 1 << i;
return res;
}
inline void dfS(int u, int fa)
{
std::vector<int> vec(V[u].size());
for (int i = 0; i < V[u].size(); ++i)
{
int v = V[u][i].first;
vec[i] = T.Ask(dfn[v], dfn[v] + siz[v] - 1);
}
for (auto v: G[u]) T.Add(dfn[v]);
for (int i = fir[u]; i; i = nex[i]) if (got[i] != fa) dfS(got[i], u);
for (int i = 0; i < V[u].size(); ++i)
{
int v = V[u][i].first, w = V[u][i].second;
if (vec[i] != T.Ask(dfn[v], dfn[v] + siz[v] - 1)) flg[w] = true;
}
}
int main()
{
int n = read();
for (int i = 2; i <= n; ++i) AddEdge(i, read());
int m = read(); dfs(1, 0);
for (int i = 1; i <= m; ++i)
{
int u = read(), v = read();
G[u].push_back(v), G[v].push_back(u);
}
int q = read(); Dfs(1, 0);
for (int i = 1, u; u = rev[i], i <= n; ++i)
for (int j = 1; j <= 20; ++j) jmp[u][j] = jmp[jmp[u][j - 1]][j - 1];
for (int t = 1; t <= q; ++t)
{
int u = read(), v = read(), lca = getlca(u, v);
if (u == v) {res[t] = flg[t] = true; continue;}
// F[u].push_back(v), F[v].push_back(u);
ans[t] = jump(u, lca) + jump(v, lca) + 1;
if (u == lca || v == lca)
{
if (u == lca) std::swap(u, v);
if (dep[jmp[u][0]] <= dep[lca]) res[t] = flg[t] = true;
}
else
{
int fu = jmp[u][0], fv = jmp[v][0];
if (dep[fu] <= dep[lca] && dep[fv] <= dep[lca]) res[t] = true;
V[u].emplace_back(v, t);
V[v].emplace_back(u, t);
}
}
dfS(1, 0);
for (int i = 1; i <= q; ++i) printf("%d\n", res[i] ? ans[i] + 1 - flg[i] : -1);
return 0;
}

浙公网安备 33010602011771号