Codeforces Round #595 (Div. 3) B—— Books Exchange (hard version)(找环/dfs记忆路径)

题目描述

题目传送门:https://codeforces.com/contest/1249/problem/B2
There are n kids, each of them is reading a unique book. At the end of any day, the i-th kid will give his book to the pi-th kid (in case of i=pi the kid will give his book to himself). It is guaranteed that all values of pi are distinct integers from 1 to n (i.e. p is a permutation). The sequence p doesn’t change from day to day, it is fixed.

For example, if n=6 and p=[4,6,1,3,5,2] then at the end of the first day the book of the 1-st kid will belong to the 4-th kid, the 2-nd kid will belong to the 6-th kid and so on. At the end of the second day the book of the 1-st kid will belong to the 3-th kid, the 2-nd kid will belong to the 2-th kid and so on.

Your task is to determine the number of the day the book of the i-th child is returned back to him for the first time for every i from 1 to n.

Consider the following example: p=[5,1,2,4,3]. The book of the 1-st kid will be passed to the following kids:

after the 1-st day it will belong to the 5-th kid,
after the 2-nd day it will belong to the 3-rd kid,
after the 3-rd day it will belong to the 2-nd kid,
after the 4-th day it will belong to the 1-st kid.
So after the fourth day, the book of the first kid will return to its owner. The book of the fourth kid will return to him for the first time after exactly one day.

You have to answer q independent queries.

Input
The first line of the input contains one integer q (1≤q≤1000) — the number of queries. Then q queries follow.

The first line of the query contains one integer n (1≤n≤2⋅105) — the number of kids in the query. The second line of the query contains n integers p1,p2,…,pn (1≤pi≤n, all pi are distinct, i.e. p is a permutation), where pi is the kid which will get the book of the i-th kid.

It is guaranteed that ∑n≤2⋅105 (sum of n over all queries does not exceed 2⋅105).

Output
For each query, print the answer on it: n integers a1,a2,…,an, where ai is the number of the day the book of the i-th child is returned back to him for the first time in this query.

Example
input
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
output
1 1 1 1 1
3 3 3
2 3 3 2 1 3
1
2 2 2 2
4 4 4 1 4

题目大意

最开始每个人手里有一本书,然后会给你一个序列,然后每个人手中的书都会在这一天结束的时候交给序列上对应位置的人,问这本书第一次回到最初的人的手上最少需要经过几天。需要注意的是:序列不会改变,即对于每个人来说,如果他手里拿的书不属于自己,他就要把这本书给所给序列对应的人,注意是所给序列!!!! 例如,所给的序列是3 4 1 2,
最初: 1 2 3 4
第一天: 3 4 1 2
第二天: 1 2 3 4
每个人都是两天,所以结果是2 2 2 2.

题目分析:

思路一:
由题意可以看出,解决此题的方式就是寻找“环”。在同一个环中的每个人的书回到自己手里所用的天数是一样的。例如:1,2,3,5是一个环,则1,2,3,5的书回到自己手里所用的天数是一样的(最后一个样例)。因此,我们可以用一个堆栈来存在同一个环内的元素。只要该数组中的元素值不等于该数组的下标,就把这个数压入堆栈,然后把该元素进行更新。注意:相等时也要压入堆栈!!因为题目计算天数的方式就是这样的。则该堆栈(该环)中元素的天数都为堆栈的大小。输出即可。

对应代码如下:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<stack>
using namespace std;
int main()
{
	int q=0,n=0,x=0;
	static int ans[200005]={0};
	static int vis[200005]={0};
	static int a[200005]={0};
	cin>>q;
	while(q--)
	{
		memset(ans,0,sizeof(ans));
		memset(a,0,sizeof(a));
		memset(vis,0,sizeof(vis));
		stack<int> s;
		int day;
		cin>>n;
		for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
		for(int i=1;i<=n;i++)
		{
			if(vis[i]!=0)
			continue;
			x=a[i];
			while(x!=i)
			{
				s.push(x);
				x=a[x];
			}
			s.push(x);
			day=s.size();
			while(!s.empty())
			{
				x=s.top();
				s.pop();
				ans[x]=day;
				vis[x]=1;
			}
		}
		for(int i=1;i<=n;i++)
		printf("%d ",ans[i]);
	}
	return 0;
}

思路二:
利用dfs进行记忆化搜索。如果数组内容和下标不对应,则递归搜索。注意每搜索一次,天数(num)就要++,然后将对应的天数用一个ans数组单独保存一下即可。其实这两种思路差不多,一个是while循环实现,一个是递归实现。
对应代码如下:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<stack>
using namespace std;
int num;
int ans[1005050];
int vis[1005050];
int a[1005050];
void dfs(int i,int j)
{
	num++;
	if(a[i]==j)
	{
		ans[i]=num;
		return;
	}
	else
	{
		dfs(a[i],j);
		ans[i]=num;
	}
	
}
int main()
{
	int q=0,n=0,x=0;
	cin>>q;
	while(q--)
	{
		num=0;
		memset(ans,0,sizeof(ans));
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
		for(int i=1;i<=n;i++)
		{
			if(ans[i]==0)
			{
				num=0;
				dfs(i,i);
			}
		}
		for(int i=1;i<=n;i++)
		printf("%d ",ans[i]);
	}
	return 0;
}

欢迎各位大佬给补充更好的思路和建议或者提出建议和意见

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值