题目描述
将 1∼n 按顺序排成一排,构成一个数列。
数字 i 刚好位于位置 i。
再给定一个长度为 n 的位置序列 p1,p2,…,pn,它是 1∼n 的一种排列。
接下来,我们会重复不断地对数列进行如下操作:
重新排列数列中每个数的位置,将位于位置 i 的数移动至位置 pi。(如果 i=pi 则该数仍移动至位置 i)。
每次操作开始时,所有数的移动同时进行,操作结束后,数列将变为一个新的 1∼n 的排列。
例如,当 n=6 并且 p=[4,6,1,3,5,2] 时,第一次操作后,数字 1 将移动至位置 4,数字 2 将移动至位置 6,以此类推;第二次操作后,数字 1 将移动至位置 3,数字 2 将移动至位置 2,以此类推。
你的任务是确定从 1 到 n 的每个数字 i,经过多少次操作后,第一次重新回到位置 i。
例如,考虑 p=[5,1,2,4,3],数字 1 的移动轨迹如下:
第一次操作后,到达位置 5。
第二次操作后,到达位置 3。
第三次操作后,到达位置 2。
第四次操作后,回到位置 1。
所以,经过四次操作后,数字 1 第一次回到位置 1。
值得一提的是,数字 4 经过一次操作后就回到了位置 4.
输入格式
第一行包含整数 T,表示共有 T 组测试数据。
每组数据第一行包含整数 n。
第二行包含 n 个整数 p1,…,pn。
输出格式
每组数据输出一行结果,包含 n 个整数,其中第 i 个整数表示数字 i 第一次回到位置 i 所经过的操作次数。
整数之间用单个空格隔开。
数据范围
对于 30% 的数据,1≤T≤10,1≤n≤10。
对于 100% 的数据,1≤T≤1000,1≤n≤2×105,1≤pi≤n。
保证 p1∼pn 是 1∼n 的一种排列。
保证 ∑n≤2×105(一个输入中的 T 个 n 相加之和不超过 2×105)。
样例
输入样例:
6
5
1 2 3 4 5
3
2 3 1
6
4 6 2 1 5 3
1
1
4
3 4 1 2
5
5 1 2 4 3
输出样例:
1 1 1 1 1
3 3 3
2 3 3 2 1 3
1
2 2 2 2
4 4 4 1 4
(暴力枚举)
回到自己的位置,有点像并查集
把这些数字看成图中一个个结点,每个结点回到自己的step,就是所在的环有多少个结点
并且:只要是这个环中的结点,step都是环的长度(环上结点个数)
时间复杂度
参考文献
C++ 代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 200010;
int n;
int p[N], s[N];
int find(int x)
{
return p[x]==x?p[x]:p[x]=find(p[x]);
}
int main()
{
int T;
scanf("%d", &T);
while (T -- )
{
scanf("%d", &n);
for (int i = 1; i <= n; i ++ ) p[i] = i, s[i] = 1;
for (int i = 1; i <= n; i ++ )
{
int j;
scanf("%d", &j);
if (find(i) != find(j))
{
s[find(j)] += s[find(i)];
p[find(i)] = find(j);
}
}
for (int i = 1; i <= n; i ++ )
printf("%d ", s[find(i)]);
puts("");
}
return 0;
}
2.dfs(暴搜)
考虑将排列看成一张图,其中点 ii 向点 pipi 连边。
那么一个排列在图上一定是很多个环。
而每个数字第一次回到自己的次数,就是其所在环的长度。
对于每个点,爆搜其所在环,求出环长,将答案扔到环中所有点上。
时间复杂度
参考文献
C++ 代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e6+10;
int n,t,p[N],res[N];
bool st[N];
int ans[N],x;
int dfs(int u)
{
if(st[u])return 0;
st[u]=true;
ans[x++]=u;
return dfs(p[u])+1;
}
int main()
{
cin>>t;
while(t--)
{
cin>>n;
memset(st, 0, sizeof st);
for(int i=1;i<=n;i++)cin>>p[i];
for(int i=1;i<=n;i++)
{
if(!st[i])
{
int t=dfs(i);
for(int k=0;k<x;k++)res[ans[k]]=t;
x=0;
}
}
for (int i = 1; i <= n; i ++ )cout<<res[i]<<" ";
puts("");
}
return 0;
}
欢迎评论点赞