0
点赞
收藏
分享

微信扫一扫

数字移动(基础(二))

mjjackey 2022-02-07 阅读 93

题目描述
将 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;
}

欢迎评论点赞

举报

相关推荐

0 条评论