The 2023 ICPC Asia Regionals Online Contest (2) E Another Bus Route Problem(补题)

Problem

Pik's village comprises of n residential buildings (labeled from 1 to n) connected by n−1 bidirectional roads. The village forms a tree structure, where each building is interpreted as a tree vertex, and each road is interpreted as a tree edge. Within the village, there are a total of m bus routes. The i-th bus route (1≤i≤m) originates from building ui​ and follows the shortest path on the tree to building vi​, and then it reverses its path to return to building ui​. When villagers wish to use the i-th bus route, they can board the bus only from either building ui​ or building vi​. They are free to disembark at any building along the shortest path between ui​ and vi​.

Pik, as the village chief, is curious about the minimum number of bus routes needed to reach any building within the village, starting from building 1 where he resides; print −1 if there is a building that cannot be reached.

Input

The first line contains two integers, n and m (1<n≤100000 and 1≤m≤100000 ), representing the number of buildings and bus routes, respectively. The following line consists of n−1 integers, where the i-th integer pi​ indicates a road connecting building pi​ and i+1. It is guaranteed that pi​≤i for 1≤i≤n−1.

Then m lines are provided, where the i-th line consists of two integers, ui​ and vi​ (ui​=vi​), indicating a bus route.

Output

One line with n−1 integers, where the i-th integer represents the minimum number of buses required to reach building i+1 from building 1. Print −1 if i+1 cannot be reached.

Sample Input

10 6
1 2 1 4 4 6 7 2 9
1 4
3 6
3 7
9 4
5 10
3 4

Sample Output

2 2 1 -1 3 3 -1 2 -1 

题意

有一个村子,里面的建筑物和路可以构成一棵无向树,有一个人站在1号节点,问从1号节点到任意一个节点,至少要乘坐几个公交车线路

输入 第一行给出n 和 m,表示有n个建筑,m条公交车路线。第二行给出n-1个数,代表pi节点和i+1之间有一条通路。其余m行每行两个数u和v,代表从u到v(v到u)是一条无向公交车路线,这条路走的是最短路径。可以从公交车经过任意节点下车,但是只能从起点u或v上车。

输出 有n - 1个数,代表从1到i+1需要乘坐多少个公交车路线

思路

首先在找最短路上,由于是树,两个节点的最短路径一定会经过他们的最近公共祖先,所以我们采用LCA来找最短路。对于给出的公交车起点终点,我们另建一个图采取从1开始bfs搜索。开一个数组记录到达每个节点的乘坐数量,公交车经过节点的数量等于起点+1,搜索过的记录一下,下次搜索到之间break,能优化掉大量时间(因为是个树,而且要到1,这个节点如果已经被搜索过,那么它以上的也被搜索过),同时公交车经过的节点要随时入队。

代码

#include<bits/stdc++.h>
using namespace std;
#define enl , writestr("\n")
#define int long long
#define endl "\n"
#define PII pair<int, int>
#define ULL unsigned long long
#define xx first
#define yy second
#define PI acos(-1)
const int P = 131;//131,13331
const int N = 1e6 + 10;
const int M = 1e3 + 10;
const int INF = 1e18;
const int ENF = -1e18;
const int mod = 1e9 + 7;//998244353
const int modd = 998244353;
void ClearFloat() { ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0); }int read() { int ret = 0, f = 1; char ch = getchar(); while ('0' > ch || ch > '9') { if (ch == '-')f = -1; ch = getchar(); }while ('0' <= ch && ch <= '9') { ret = ret * 10 + ch - '0'; ch = getchar(); }return ret * f; }void write(int x) { char num[30]; int cnt = 0; if (x == 0) { putchar('0'); return; }if (x < 0) { putchar('-'); x = -x; }while (x > 0) { num[cnt++] = x % 10 + '0'; x /= 10; }while (cnt > 0) { putchar(num[--cnt]); } }double readdou() { double x = 0; int flag = 0; char ch = 0; while (!isdigit(ch)) { flag |= (ch == '-'); ch = getchar(); } while (isdigit(ch)) { x = x * 10 + (ch - '0'); ch = getchar(); }if (ch != '.') return flag ? -x : x; int f = 1; ch = getchar(); while (isdigit(ch)) { x = x + (ch - '0') * pow(10, -f); f++; ch = getchar(); }return flag ? -x : x; }void wridou(int x) { char num[30]; int cnt = 0; if (x == 0) { putchar('0'); return; }if (x < 0) { putchar('-'); x = -x; }while (x > 0) { num[cnt++] = x % 10 + '0'; x /= 10; }while (cnt > 0) { putchar(num[--cnt]); } }int gcd(int a, int b) { if (b) while ((a %= b) && (b %= a)); return a + b; }int lcm(int a, int b) { return a * b / gcd(a, b); }
int e[N], h[N], f[N][25], idx, ne[N], n, m, d[N], t, fat[N], res[N];//res数组为答案,fat数组记录父节点
queue<int>q;
bool st[N];
vector<int>vt[N];
void add(int a, int b)
{
    e[++idx] = b, ne[idx] = h[a], h[a] = idx;
}
void bfs()
{
    queue<int>q;
    q.push(1); d[1] = 1;
    while (q.size())
    {
        int x = q.front(); q.pop();
        for (int i = h[x]; i; i = ne[i])
        {
            int y = e[i];
            if (d[y]) continue;
            fat[y] = x;
            d[y] = d[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);
        }
    }
}
int lca(int x, int y)
{
    if (d[x] > d[y])swap(x, y);
    for (int i = t; i >= 0; i--)
        if (d[f[y][i]] >= d[x])y = f[y][i];
    if (x == y)return x;
    for (int i = t; i >= 0; i--)
    {
        if (f[x][i] != f[y][i])
        {
            x = f[x][i], y = f[y][i];
        }
    }
    return f[x][0];
}
signed main()
{
    ClearFloat();
    cin >> n >> m;
    t = (log(n) / log(2)) + 1;
    for (int i = 2; i <= n; i++)
    {
        int x;
        cin >> x;
        add(x, i), add(i, x);
    }
    bfs();
    for (int i = 1; i <= m; i++)//将公交车起点终点另外建图
    {
        int u, v;
        cin >> u >> v;
        vt[u].push_back(v), vt[v].push_back(u);
    }
    q.push(1);//从1开始搜索公交车路线
    while (q.size())
    {
        int t = q.front();
        q.pop();
        st[t] = true;
        for (int i = 0; i < vt[t].size(); i++)//搜索以当前点为起点的终点,并求出这条路线上的答案
        {
            int it = vt[t][i];//终点
            if (!st[it])
            {
                q.push(it);//终点入队
                int op = lca(t, it);//求出最近公共祖先
                int a = fat[it], b = fat[t];//分别从它们的父节点开始向上搜索到1
                if (!st[it]) res[it] = res[t] + 1, st[it] = true;//公交车上的点等于起点+1
                while (a != 1)//两个bfs分别表示从终点和起点搜索到1,已经搜索到就break
                {
                    if (st[a]) break;
                    st[a] = true;
                    q.push(a);
                    res[a] = res[t] + 1;
                    if (a == op) break;
                    a = fat[a];
                }
                while (b != 1)
                {
                    if (st[b]) break;
                    st[b] = true;
                    q.push(b);
                    res[b] = res[t] + 1;
                    if (b == op) break;
                    b = fat[b];
                }
            }
        }
    }
    for (int i = 2; i <= n; i++)
    {
        if (!res[i]) cout << -1 << ' ';
        else cout << res[i] << ' ';
    }
}

  • 15
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值