LG2921 [USACO2008DEC]Trick or Treat on the Farm 内向基环树
问题描述
题解
发现一共有 \(n\) 个点,每个点只有一条出边,即只有 \(n\) 条边,于是就是一个内向基环树。
\(\mathrm{Tarjan}\) 缩点。
但是这个题比较猥琐的就是有自环。
所以断定一个强联通分量 \(i\) 是环的条件是 \(size_i>1\) 。
然后记搜求答案,特判自环。
\(\mathrm{Code}\)
#include<bits/stdc++.h>
using namespace std;
template <typename Tp>
void read(Tp &x){
x=0;char ch=1;int fh;
while(ch!='-'&&(ch>'9'||ch<'0')) ch=getchar();
if(ch=='-') ch=getchar(),fh=-1;
else fh=1;
while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
x*=fh;
}
const int maxn=100007;
int n;
int son[maxn],fa[maxn];
int dfn[maxn],low[maxn],ind;
bool ins[maxn];
int sta[maxn],top;
int bel[maxn],cnt;
int size[maxn];
int spe;
void tarjan(int x){
dfn[x]=low[x]=++ind,ins[x]=1,sta[++top]=x;
if(dfn[son[x]]){
if(ins[son[x]]) low[x]=min(low[x],dfn[son[x]]);
}
else{
tarjan(son[x]);
low[x]=min(low[x],low[son[x]]);
}
if(dfn[x]==low[x]){
++cnt;
while(sta[top]!=x){
bel[sta[top]]=cnt;
ins[sta[top]]=0;--top;
++size[cnt];
}
++size[cnt];
ins[x]=0,bel[x]=cnt;--top;
}
}
int ans[maxn];
int dfs(int x){
if(size[bel[x]]>1) return ans[x]=size[bel[x]];
if(ans[x]) return ans[x];
return ans[x]=dfs(son[x])+1;
}
int main(){
read(n);
for(int i=1;i<=n;i++){
read(son[i]);fa[son[i]]=i;
if(son[i]==i) ans[i]=1;
}
for(int i=1;i<=n;i++){
if(!dfn[i]) tarjan(i);
if(size[bel[i]]>1) spe=bel[i];
}
for(int i=1;i<=n;i++){
if(!ans[i]) dfs(i);
}
for(int i=1;i<=n;i++)
printf("%d\n",ans[i]);
return 0;
}