分析
这很明显是一道图论题,但十分特殊的是每个点都只有一条出边,也就是出度为1,所以这个图只可能出现链和环的组合。
比如这样:
那么我们就可以想到用dfs搜出一个环之后,对其进行回溯时的赋值
首先当一条链和一个环组合时,环中的所有点答案都相同,而链上的点的答案为环的大小(节点个数)加上这个点到环的距离,而这个点到环的距离又正好是前一个回溯过来的链上的点的距离+1,所以我们只要能判断出一个点是在链还是环上,之后就十分简单了
那么对于这个问题,我们只需要引入一个数组dis记录每个点到dfs起点的距离,并且记录环的起点,对于一个点x,设环的起点为st,若则是在环上的,否则是在链上的
还有一个细节:当dfs过程中发现一个点已经访问过的时候,不一定是找到了一个环,也有可能是上一次dfs后记录的答案,这种情况只需要每次dfs前清空dis数组,遇到vis==1的时候 判断dis是否被赋值就可以了,如果已经被赋值了,说明是本次dfs中的环,否则直接返回已经存储的答案。
代码
#include <bits/stdc++.h>
#define MAX 100005
using namespace std;
int dis[MAX], Next[MAX], vis[MAX], ans[MAX];
int n, circle, st;
int dfs(int x, int dist){
if(vis[x]){
if(dis[x] == 0){
dis[x] = dist;
st = x;
return ans[x];
}
circle = dist - dis[x];
st = x;
return circle;
}
dis[x] = dist;
vis[x] = 1;
ans[x] = dfs(Next[x], dist+1) + (int)((dis[st] - dis[x])>0);
return ans[x];
}
int main()
{
cin >> n;
for(int i = 1; i <= n; i++){
scanf("%d", &Next[i]);
}
for(int i = 1; i <= n; i++){
if(vis[i]){
cout << ans[i] << endl;
}
else{
memset(dis,0,sizeof(dis));
cout << dfs(i,1) << endl;
}
}
return 0;
}