1. 程式人生 > >P2921 [USACO08DEC]在農場萬聖節

P2921 [USACO08DEC]在農場萬聖節

一道不錯的記搜題。

由於每一個人點的出度只能是1,所以我們不難發現:每一條在環上的邊只能屬於一條環。換句話說:任何一條邊頂多屬於一個環。這樣我們就可以用簡單的記搜來實現。

f[i]為從點i開始最多能到達的點的總個數,那麼顯然,當i在某個環中,f[i]=其他環上的點的f的值;當i不在環中時,f[i]=從i出發最先能到達的環中的點ji的距離+f[j]

具體的實現細節見程式碼。

Code:

#include<cstdio>
#include<iostream>
using namespace std;

const int MAXN=100020;
int n,nxt[MAXN],fst,dfn[MAXN],huan[MAXN],f[MAXN];

inline int read()
{
	int x=0;
	char ch=getchar();
	while(ch<'0'||'9'<ch)	ch=getchar();
	while('0'<=ch&&ch<='9')
	{
		x=(x <<3)+(x <<1)+(ch-'0');
		ch=getchar();
	}
	return x;
}

void dfs(int x,int tim)
{
	if(nxt[x]==x)	{ f[x]=1; return; }//一定記得判斷自環的情況 
	if(f[nxt[x]]>0)  { f[x]=f[nxt[x]]+1; return; }
	dfn[x]=tim;
	if(dfn[nxt[x]]>0)	
	{ 
		huan[x]=1; fst=nxt[x];//huan變數幫助判斷當前點是否在環上 
		f[x]=dfn[x]-dfn[nxt[x]]+1; 
		//記錄dfs序:當某個點通往一個已被搜到的點nxt[x],說明從nxt[x]到x的路徑+單向邊w(x,nxt[x])已經構成了一個環。
		//顯然這個環的長度=dfn[x]-dfn[nxt[x]]+1;  
		return;
	}
	dfs(nxt[x],tim+1);
	if(huan[nxt[x]]==1)	 
	{ 
		huan[x]=1; f[x]=f[nxt[x]]; 
		if(x==fst)	huan[x]=0;
		return; 
	}
	f[x]=f[nxt[x]]+1;
	return;
}

int main()
{
	n=read();
	for(int i=1;i<=n;i++)	nxt[i]=read();
	for(int i=1;i<=n;i++)
	{
		fst=0;
		if(!dfn[i])	 dfs(i,1);
	}
	for(int i=1;i<=n;i++)	cout<<f[i]<<'\n';
	return 0;
}