Rendezvous

题面

给定一个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;
}
posted @ 2020-08-30 09:25  洛绫璃  阅读(361)  评论(0编辑  收藏  举报