AcWing 3579. 数字移动 带注释
原题链接
困难
作者:
蓬蒿人
,
2021-06-02 18:43:16
,
所有人可见
,
阅读 380
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
// 整体思路
// 经过模拟我们发现似乎移动只发生在某一个环内
// 这一组的数字的下标组成了环内数字要回到正确位置要走的路径
// 因而我们猜想 n个数字可以被分为m个环
// 各组数字回到正确位置要走的次数 就是环内的元素个数
// 如此 描述 就是并查集了 给每个元素归入对应集合 最后输出元素对应的集合元素总数
int T,p[200010],s[200010],n;
//p数组存归属集合 s数组存集合i的元素个数
int find(int x){
//并查集模板
if (x!=p[x]) p[x]=find(p[x]);
return p[x];
}
int main()
{
cin>>T;
while (T--){
cin>>n;
for (int i=1;i<=n;i++){
s[i]=1;
p[i]=i;
//先初始化一下 初始时每个集合都只有自己
}
for(int i=1;i<=n;i++){
int j;
scanf ("%d",&j);
// 因为环还是环内元素 回到正确位置的路径的集合
// 我们不断地读入 j其实是在不断地维护环(find(j))
if (find(i)!=find(j)){
//若i j对应集合不同 说明有两个集合需要合并 本题解 以i对于的集合为主
s[find(i)]+=s[find(j)];//首先更新find(i)环集合的数量
p[find(j)]=find(i);//然后更新find(j)的对应集合
// 注意上下两行不能交换 哦
// 若先更新对应集合 s[find(j)]就等于s[find(i)]了 s数组无法正确更新了
}
// 为什么i与j对应的集合不同就要合并呢 ?
// 因为i作为 j的下标 必然属于一个环内 (不懂请手动模拟样例,在下不会证明2333)
}
for (int i=1;i<=n;i++) printf ("%d ",s[find(i)]);
//最后输出
cout<<'\n';
}
return 0;
}
蓬蒿人❥!!!