测评会员优惠活动进行中 · 开通 VIP,有效期内测评不限次 VIP 优惠中 · 测评不限次 立即查看

A17497. 传话对象

填空题 困难

题目描述

传话对象

题目描述

在一个公司里,有 n 名员工,编号 1 到 n。每名员工都有一个“传话对象”,即第 ii 名员工只会把消息告诉第 ti 名员工(允许告诉自己)。现在,从每名员工出发,依次沿着传话对象传递消息,可以证明经过有限步后,消息一定会回到一个已经传过的员工。

请你分别计算:从第 i 名员工开始,需要传递多少步后,才会第一次遇到一个已经传过消息的员工。

输入格式

第一行,一个整数 n。

第二行,n 个整数 t1,t2,…,tn,表示第 i 名员工的传话对象。

输出格式

输出 n 行,第 i 行一个整数,表示从第 i 名员工出发的答案。

输入样例

4
2 1 1 4

输出样例

2
2
3
1

说明提示

1≤n≤5×105

参考答案

#include <iostream> #include <vector> using namespace std; using ll = long long; const int MAXN = 5e5 + 10; int t[MAXN]; int vis[MAXN]; // 0未访问 1访问中 2已处理 int ans[MAXN]; vector<int> path; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; cin >> n; for (int i = 1; i <= n; i++) { cin >> t[i]; } for (int i = 1; i <= n; i++) { if (vis[i] != 0) continue; path.clear(); int cur = i; while (true) { if (vis[cur] == 1) { // 找到环起点:cur在path中的位置 int pos = 0; while (path[pos] != cur) pos++; // 环部分 [pos, end] int ring_len = path.size() - pos; for (int j = pos; j < path.size(); j++) { ans[path[j]] = ring_len; vis[path[j]] = 2; } // 树链部分 [0, pos-1] 倒推 for (int j = pos - 1; j >= 0; j--) { ans[path[j]] = ans[t[path[j]]] + 1; vis[path[j]] = 2; } break; } if (vis[cur] == 2) { // 当前路径全部是树链,用已处理点倒推 for (int j = path.size() - 1; j >= 0; j--) { ans[path[j]] = ans[t[path[j]]] + 1; vis[path[j]] = 2; } break; } // 未访问,加入路径标记为访问中 vis[cur] = 1; path.push_back(cur); cur = t[cur]; } } for (int i = 1; i <= n; i++) { cout << ans[i] << '\n'; } return 0; }
上一题 下一题