LG2921 [USACO2008DEC]Trick or Treat on the Farm 内向基环树

问题描述

LG2921


题解

发现一共有 \(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;
}
posted @ 2019-10-18 18:47  览遍千秋  阅读(142)  评论(0编辑  收藏  举报