Trick or Treat on the Farm(Tarjan缩点题)

 题目描述


  一张\(n\)个点的有向图,\(n\)个奶牛分别从\(n\)个点出发,每经过一个点答案就\(+1\),直到走到之前走过的点,问答案最大是多少。

思路


  1.奶牛回到已经过的点后会停止,那么就是进入到了一个环。

  2.见到环要缩点,并记录个个环的大小。

  3. 点无非就三种:

    (1)在环内的点,它们的\(ans=环的大小\)

    (2)在环外的点,它们的\(ans=环的大小+到环的距离\)

    (3)环大小为\(1\)时,\(ans=1\)

代码


#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn=1e5+5;
ll n,cnt,num,rt,dfn[maxn],low[maxn],scc[maxn],val[maxn],ans[maxn];
vector<ll> e[maxn];
map<pair<ll,ll>,bool> mp;
stack<ll> stk;
bool isin[maxn];
void dfs1(ll x){
	dfn[x]=low[x]=++cnt;
	isin[x]=true;
	stk.push(x);
	for(auto y:e[x]){
		if(!dfn[y]){
			dfs1(y);
			low[x]=min(low[x],low[y]);
		}else if(isin[y]){
			low[x]=min(low[x],dfn[y]);
		}
	}
	if(dfn[x]==low[x]){
		num++;
		ll y;
		do{
			y=stk.top();
			stk.pop();
			scc[y]=num;
			isin[y]=false;
		}while(y!=x);
	}
}
void dfs2(ll x,ll step){
	for(auto y:e[x]){
		if(ans[y]==0){
			dfs2(y,step+1);
		}else{
			ans[rt]=ans[y]+step;
			return;
		}
	}
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cin>>n;
	for(ll i=1;i<=n;i++){
		ll x;
		cin>>x;
		if(i==x){
			ans[i]=1;
		}else if(!mp[{i,x}]){
			mp[{i,x}]=true;
			e[i].push_back(x);
		}
	}
	for(ll i=1;i<=n;i++){
		if(!dfn[i]){
			dfs1(i);
		}
	}
	for(ll i=1;i<=n;i++){
		val[scc[i]]++;
	}
	for(ll i=1;i<=n;i++){
		if(val[scc[i]]!=1){
			ans[i]=val[scc[i]];
		}
	}
	for(ll i=1;i<=n;i++){
		if(ans[i]==0){
			rt=i;
			dfs2(i,1);
		}
	}
	for(ll i=1;i<=n;i++){
		cout<<ans[i]<<"\n";
	}
	return 0;
}
posted @ 2026-10-17 16:27  beiciqi  阅读(4)  评论(0)    收藏  举报