现充|junyu33

题解 P2921 【[USACO08DEC]在农场万圣节Trick or Treat on the Farm】

其实这道题不需要tarjan,不需要分类讨论,只需要记一下时间戳、环的周长即可.

先用dfs1预处理各个环的周长(包括自环),并把周长的值d标记在整个环上。再用dfs2遍历路线,待到d值为正时,直接返回搜索深度+d.

具体细节看代码(核心部分不超20行)

#include <bits/stdc++.h>
#define inf 5201314
using namespace std;
const int N=3e5+10;
int n,to[N],d[N],dfn[N];
void dfs1(int a,int t){
   if(d[a]||dfn[a]>=inf) return; //避免第三次遍历环,同时避免给环外的节点错误赋d值 
   if(dfn[a]&&dfn[a]<inf) d[a]=t-dfn[a];//第二次遍历环即可利用时间戳之差计算环的周长 
   else dfn[a]=t;
   dfs1(to[a],t+1);
   dfn[a]=inf; //遍历完成后,给路径赋一个极大值,标记此环已经来过
}
int dfs2(int a){
   if(d[a]) return d[a];
   return d[a]=dfs2(to[a])+1;//路径压缩,节省大量时间 
}
int main(){
   cin>>n;
   for(int i=1;i<=n;i++) cin>>to[i];
   for(int i=1;i<=n;i++){
      if(dfn[i]) continue;//说明路径已走过,节省大量时间  
      dfs1(i,1);
   }
   for(int i=1;i<=n;i++) cout<<dfs2(i)<<endl;
   return 0;
}