算法
(dfs,爆搜)
考虑将排列看成一张图,其中点 $i$ 向点 $p _ i$ 连边。
那么一个排列在图上一定是很多个环。
而每个数字第一次回到自己的次数,就是其所在环的长度。
对于每个点,爆搜其所在环,求出环长,将答案扔到环中所有点上。
时间复杂度
$\text{dfs}$ 中会将所有点都枚举一次,故复杂度为 $O(n)$。
C++ 代码
#include <cstdio>
#include <cstring>
const int N = 200005;
int n, p[N], res[N];
bool st[N];
int xs[N], sz;
// 返回 x 所在环长
int dfs(int x) {
if (st[x]) return 0;
st[x] = true;
xs[sz++] = x; // 将环上所有点存到一个数组中
return dfs(p[x]) + 1;
}
int main() {
int task;
for (scanf("%d", &task); task--; ) {
scanf("%d", &n);
memset(st, false, sizeof st);
for (int i = 1; i <= n; ++i) scanf("%d", p + i);
for (int i = 1; i <= n; ++i)
if (!st[i]) {
int t = dfs(i);
for (int k = 0; k < sz; ++k) res[xs[k]] = t;
sz = 0;
}
for (int i = 1; i <= n; ++i) printf("%d ", res[i]);
puts("");
}
return 0;
}
贴一个python的
## 并查集求连通块大小
### 直接模拟 + 剪枝优化