給定一棵內向森林(每個節點出度 ≤ 1),多次查詢:給兩點 a, b,求非負整數對 (x, y) 使從 a 走 x 步、從 b 走 y 步到達同一點。優先條件:
1. 存在解。
2. max(x, y) 最小。
3. min(x, y) 最小。
4. x ≥ y(為了輸出唯一,非題目原意)。
root[x]:從 x 出發第一個到達的環上節點。root[a] != root[b] 且不在同一環 → 無解。root[a] 或 root[b],分別計算步數取最優。預處理 fa[u][k](2k 祖先)與 dpt[u](到環距離)。環上節點深度為 0,fa[u][0] 指向環上下一節點。
#include <bits/stdc++.h>
using namespace std;
const int M = 500500;
int n, q, T;
int a[M], v[M], on_ring[M], pos[M], sz[M];
int fa[M][20], dpt[M], root[M];
void dfs(int x) {
v[x] = T;
if (v[a[x]] == T) { // 找到環
int len = 1;
for (int i = a[x]; i != x; i = a[i]) ++len;
for (int i = x; ; i = a[i]) {
on_ring[i] = T; pos[i] = len; root[i] = i; sz[T] = len;
if (a[i] == x) break;
}
return;
}
if (!v[a[x]]) dfs(a[x]);
if (!on_ring[x]) {
fa[x][0] = a[x];
dpt[x] = dpt[a[x]] + 1;
root[x] = root[a[x]];
}
}
int lca(int x, int y) {
if (dpt[x] < dpt[y]) swap(x, y);
for (int k = 19; k >= 0; --k)
if (dpt[fa[x][k]] >= dpt[y]) x = fa[x][k];
if (x == y) return x;
for (int k = 19; k >= 0; --k)
if (fa[x][k] != fa[y][k]) x = fa[x][k], y = fa[y][k];
return fa[x][0];
}
int dist_on_ring(int x, int y, int p) { // 環上從 x 到 y 步數
return (y - x + p) % p;
}
void solve(int x, int y) {
if (on_ring[root[x]] != on_ring[root[y]]) { printf("-1 -1\n"); return; }
if (root[x] == root[y]) {
int z = lca(x, y);
printf("%d %d\n", dpt[x] - dpt[z], dpt[y] - dpt[z]);
return;
}
int rx = root[x], ry = root[y];
int p = sz[on_ring[rx]];
int x1 = dpt[x] + dist_on_ring(pos[rx], pos[ry], p), y1 = dpt[y];
int x2 = dpt[x], y2 = dpt[y] + dist_on_ring(pos[ry], pos[rx], p);
// 比較 (max, min, x>=y)
if (max(x1,y1) != max(x2,y2)) { if (max(x1,y1) < max(x2,y2)) printf("%d %d\n", x1, y1); else printf("%d %d\n", x2, y2); }
else if (min(x1,y1) != min(x2,y2)) { if (min(x1,y1) < min(x2,y2)) printf("%d %d\n", x1, y1); else printf("%d %d\n", x2, y2); }
else printf("%d %d\n", max(x1,y1), min(x1,y1));
}
int main() {
ios::sync_with_stdio(false); cin.tie(0);
cin >> n >> q;
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
for (int i = 1; i <= n; ++i) if (!v[i]) ++T, dfs(i);
for (int k = 1; k < 20; ++k)
for (int i = 1; i <= n; ++i) fa[i][k] = fa[fa[i][k-1]][k-1];
while (q--) { int x, y; scanf("%d%d", &x, &y); solve(x, y); }
return 0;
}
pos、樹深度 dpt、根標記 root。(y - x + p) % p。