赛时不停的找环以及用并查集把自己卡sb了。赛后一发入魂。

题意:

给一个序列,问你从某个位置开始传书后,要传几次才能回到自己手上

q次询问,总共有n(n <= 2e5)个序列

 

题解:

模拟下样例会发现是个环,所以从某个位置开始传导后,它所到达的所有位置,从这所有位置开始后,再回到自己手上都需要相同的次数,因此可以在O(n)的情况下处理出所有存在于一个集合的数,输出即可。

 

代码:

#include<bits/stdc++.h>

using namespace std;

const int N = 2e5 + 10;
int p[N], anc[N], ans[N];
bool st[N];
int n, q;

//fir设定为集合的公共祖先,类似并查集
//anc存储它们的祖先
//ans主要存储集合的数个数
void find(int fir){
	int t = p[fir];
	while(t != fir){
		ans[fir]++;
		st[t] = true;
		anc[t] = fir;
		t = p[t];
	}
}

int main()
{
	scanf("%d", &q);
	while(q--){
		scanf("%d", &n);
		
		memset(st, false, (n + 5) * sizeof(bool));
		for(int i = 1; i <= n; i++)
			scanf("%d", &p[i]);
			
		for(int i = 1; i <= n; i++){
                        //只有未被纳入某个集合才能
			if(!st[i]){
				ans[i] = 1;
				st[i] = true;
				anc[i] = i;
				find(i);
			}
		}
		
		for(int i = 1; i <= n; i++)
			printf("%d%s", ans[anc[i]], (i == n) ? "\n" : " ");
	}
	
	return 0;
}