题面
给定一个n个顶点的有向图,每个顶点有且仅有一条出边。
对于顶点i,记它的出边为(i, a[i])。
再给出q组询问,每组询问由两个顶点a、b组成,要求输出满足下面条件的x、y:
从顶点a沿着出边走x步和从顶点b沿着出边走y步后到达的顶点相同。
在满足条件1的情况下max(x,y)最小。
在满足条件1和2的情况下min(x,y)最小。
在满足条件1、2和3的情况下x>=y。
如果不存在满足条件1的x、y,输出-1 -1。
输入格式
第一行两个正整数n和q。
第二行n个正整数a[1],a[2],…,a[n]。
下面q行,每行两个正整数a,b,表示一组询问。
输出格式
输出q行,每行两个整数。
数据范围
n,q≤500000,
a[i]≤n,
a,b≤n
输入样例:
12 5
4 3 5 5 1 1 12 12 9 9 7 1
7 2
8 11
1 2
9 10
10 5
输出样例:
2 3
1 2
2 2
0 1
-1 -1
题解
这道题和那道基环树森林是类似的, 从在子树上 和 在环上 考虑
先用dfs, 跑同一个基环树上的所有点, 并找出环
我们使用倍增的方法, 处理环上的每棵子树
对于两个节点, 且二者不在同一颗基环树上, 明显 -1, -1
然后就是倍增找父节点,
直接通过 较大深度的节点 向上走 走到x 的, x y同一颗子树, 答案明显 二者深度分别减去 lca深度
然后是 两者在同意深度, 向上同时走
1.最后按通常情况 f[x][0], 应该是lca, 但是我们由于x, y可能在不同的子树上,
所以当 f[x][0] != 0, 是 lca = f[x][0], 答案和上一样, x y同一颗子树
2.f[x][0] == 0, 明显 x, y都走到了自己所在子树的根节点, 也就是环上
那么按照题意, 选择是让 x -> y, 还是 y -> x, 计算答案即可
#include <bits/stdc++.h>
#define all(n) (n).begin(), (n).end()
#define se second
#define fi first
#define pb push_back
#define mp make_pair
#define sqr(n) (n)*(n)
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define per(i,a,b) for(int i=a;i>=(b);--i)
#define IO ios::sync_with_stdio(0); cin.tie(0);
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
typedef pair<ll, ll> PLL;
typedef vector<int> VI;
typedef double db;
const int N = 5e5 + 5;
int n, m, _, k, t;
int h[N], to[N << 1], ne[N << 1], tot;
int f[N][20], dep[N];
int a[N], idx[N], idcnt, idc[N], sizc[N];
int cir[N], cid;
bool v[N];
void add(int u, int v) {
ne[++tot] = h[u]; h[u] = tot; to[tot] = v;
}
void bfs(int s) {
queue<int> q; q.push(s); dep[s] = 1;
while (!q.empty()) {
int x = q.front(); q.pop();
for (int i = h[x]; i; i = ne[i]) {
if (!(i & 1)) continue;
int y = to[i];
if (v[y] || dep[y]) continue;
dep[y] = dep[x] + 1; f[y][0] = x;
for (int j = 1; j <= t; ++j) f[y][j] = f[f[y][j - 1]][j - 1];
q.push(y);
}
}
}
PII lca(int x, int y) {
if (idx[x] != idx[y]) return { -1, -1 };
int a = x, b = y; bool flag = 0;
if (dep[x] > dep[y]) swap(x, y), flag = 1;
per(i, t, 0) if (dep[f[y][i]] >= dep[x]) y = f[y][i];
if (x == y) return { dep[a] - dep[x], dep[b] - dep[x] };
per(i, t, 0) if (f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
if (f[x][0]) return { dep[a] - dep[f[x][0]], dep[b] - dep[f[x][0]] };
x = idc[x], y = idc[y];
if (flag) swap(y, x);
PII p = { dep[a] - 1, dep[b] - 1 };
int bd = y > x ? sizc[idx[a]] - y + x : x - y;
int ad = y > x ? y - x : sizc[idx[a]] - x + y;
if (max(p.fi + ad, p.se) < max(p.se + bd, p.fi)) p.fi += ad;
else if (max(p.se + bd, p.fi) < max(p.fi + ad, p.se)) p.se += bd;
else if (min(p.se, p.fi + ad) > min(p.fi, p.se + bd)) p.se += bd;
else p.fi += ad;
return p;
}
int dfsc(int u, int bian) {
idx[u] = idcnt; int s = 0;
for (int i = h[u]; i; i = ne[i]) {
int y = to[i];
if (bian == i + 1 >> 1 || v[y]) continue;
if (!idx[y]) s = max(s, dfsc(y, i + 1 >> 1));
else cir[idc[s = u] = ++cid] = u, v[u] = 1;
}
return s;
}
void work(int u) {
cid = 0; ++idcnt;
for (u = a[dfsc(u, 0)];u != cir[1]; u = a[u])
cir[idc[u] = ++cid] = u, v[u] = 1;
sizc[idcnt] = cid;
rep(i, 1, cid) bfs(cir[i]);
}
int main() {
IO; cin >> n >> m;
t = log2(n - 1) + 1;
rep(i, 1, n) cin >> a[i], add(a[i], i), add(i, a[i]);
rep(i, 1, n) if (!idx[i]) work(i);
rep(i, 1, m) {
int x, y; cin >> x >> y;
PII p = lca(x, y);
cout << p.fi << ' ' << p.se << '\n';
}
return 0;
}