E
-
构建两个无向图,一个存路径,一个存公交站点
-
预处理路径,类似并查集,不需要寻找祖宗节点,只需要一层层记录每一个点的父节点
-
开pair优先队列升序排列,存入到达点的转车次数和目标点,排序是升序,所以保证队头为最小,直到队头不为0,就算找到所有点的转车次数
-
BFS,不断压入公交站点(只压入公交站点),
- 当该点已经找到转车次数时跳过处理该点
- 当转到一个站点时,向上遍历父节点,这些节点均为站点路径
/*测试样例
10 6
1 2 1 4 4 6 7 2 9
1 4
4 3
3 6
3 7
9 4
5 10
*/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 1000;
typedef pair<int, int> PII;
int n, ans[N], m;
vector<int> go[N], edge[N];
priority_queue<PII, vector<PII>, greater<PII>> q;//升序优先队列
int f[N];
void dfs(int u, int fa) { //预处理出每个节点的父节点
f[u] = fa;
for (auto v : go[u]) {
if (v == fa) continue;
dfs(v, u);
}
}
void solved(){
cin >> n >> m;
for (int i = 2; i <= n; i ++) {
int u;
cin >> u;
go[u].emplace_back(i);//vector存路径,无向图
go[i].emplace_back(u);
}
for (int i = 1; i <= m; i ++) {
int u, v;
cin >> u >> v;
edge[u].emplace_back(v);//vector存公交站
edge[v].emplace_back(u);
}
dfs(1, 1);
q.push({0, 1});//压入初始点,1站点转车次数为0
ans[1] = 1;
while (!q.empty()) { //压入队的一定是公交站点
PII tmp = q.top();
q.pop();
int u = tmp.second, w = tmp.first;//u为节点位置,w为到达当前节点转车次数
for (auto v : edge[u]) {//遍历当前车站所连接的其他车站
if (ans[v]) continue;//当前目标点已经找到答案
while(!ans[v]){//在向上寻找父节点的过程中直到找到另一个站点或已经有答案的点停止
if(!edge[v].empty()) q.push({w + 1, v});//该点为车站点,因为只有车站点不为空, 压入队列表示转站次数+1
ans[v] = w + 1;//当前点读入转车次数
v = f[v];//向上寻找父节点
}
}
}
for (int i = 2; i <= n; i ++) {
if (!ans[i]) ans[i] = -1;
cout << ans[i] << " ";
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0), std::cout.tie(0);
int t;
t = 1;
while (t -- )
solved();
return 0;
}