1. 程式人生 > 實用技巧 >[洛谷P5049][洛谷P5022][題解]旅行

[洛谷P5049][洛谷P5022][題解]旅行

0.一些東西

原題
資料加強版
加強版程式碼參考你谷題解
終於調過了(又是一如既往的申必錯誤)
€€£ NOI plus石錘了

1.題解

原題的資料允許我們\(O(n^2)\)暴力斷邊,但是加強版的資料達到了\(n\log n\)級別,我們必須在斷邊這一環節尋求更好的解法。
考慮我們進入環後在何處回溯(根據繼續走環走到的點分類):

(設當前已經從\(b\)走到\(c\)
1.編號最小(\(d<e,f\)):顯然可以直接繼續走;
2.編號非最大/最小(\(e<d<f\)):按大小順序走完支鏈再來;
3.編號最大(\(d>e,f\)):需要分兩種情況討論,
①出邊就是最大的(\(d>b\)

),此時回溯更優;
②回溯後的比出邊大(\(b>d\)),只能硬著頭皮走下去。
所以說在走的時候需要記錄一下最大點,這時候把邊按照終點排序會很有幫助。

2.細節

1.只能回溯一次,注意標記(程式碼中back);
2.注意一些功能的實現(如找環、排邊等等)。

3.程式碼

(附註釋)
(預設源在置頂部落格)

#define N 500010
int n,m,ans[N],tot,back,mx,isring;
int vis[N],rin[N],fa[N];
struct Input {
	int frm,to;
	Input(int _u=0,int _v=0){
		frm=_u,to=_v;
	}
}ipt[N<<1];
bool operator < (Input a,Input b){
	return a.to>b.to;
}
struct Edge {
	int to,nxt;
}e[N<<1];
int head[N],cnt;
inline void ade(int u,int v){
	e[++cnt].to=v;
	e[cnt].nxt=head[u];
	head[u]=cnt;
}
void DFS1(int now){//處理樹的情況 
	ans[++tot]=now,vis[now]=1;
	for(rg int i=head[now];i;i=e[i].nxt){
		int v=e[i].to;
		if(!vis[v])DFS1(v);
	}
}
void DFS_Ring(int now,int ff){//找環 
	if(isring)return;//找到環的標記 
	if(!fa[now])fa[now]=ff;
	else if(fa[now]!=ff){//找到環了 
		while(ff!=now){//不斷回溯回去 
			rin[ff]=1;
			ff=fa[ff];
		}
		rin[now]=isring=1;
		return;
	}
	for(rg int i=head[now];i;i=e[i].nxt){
		int v=e[i].to;
		if(v!=ff)DFS_Ring(v,now);
	}
}
void DFS2(int now){//處理基環樹的情況 
//	cout<<"search "<<now<<endl;
	ans[++tot]=now,vis[now]=1;
	if(rin[now]){//在環上,特殊處理 
		int frog=0;//是否需要回溯(第三種情況) 
		for(rg int i=head[now];i;i=e[i].nxt){
			if(back)break;//不需要回溯了 
			int v=e[i].to;
			if(!vis[v]){
				if(rin[v]){//終點在環上的出邊 
					i=e[i].nxt;
					while(vis[e[i].to])i=e[i].nxt;//不斷跳訪問過的 
					if(i)mx=e[i].to;//不是最大的出邊 
					else if(v>mx)frog=back=1;//是最大的,回溯
					//此處運用到了排序後邊的順序 
					break;
				}
			}
		}
		for(rg int i=head[now];i;i=e[i].nxt){
			int v=e[i].to;
//			printf("vis[%d]=%d,rin[%d]=%d,frog=%d\n",v,vis[v],v,rin[v],frog);
			if(!vis[v]){
				if(!rin[v]||!frog)DFS2(v);//不需要回溯(注意||的使用) 
			}
		}
	}else {//不在環上的正常走即可 
		for(rg int i=head[now];i;i=e[i].nxt){
			int v=e[i].to;
			if(!vis[v])DFS2(v);
		}
	}
}
int main(){
	Read(n),Read(m);
	for(rg int i=1;i<=m;i++){
		int u,v;
		Read(u),Read(v);
		ipt[i]=Input(u,v),ipt[i+m]=Input(v,u);//先存一下 
	}
	sort(ipt+1,ipt+1+2*m);
	for(rg int i=1;i<=2*m;i++)ade(ipt[i].frm,ipt[i].to);//排序後加邊 
	if(m==n-1){
		DFS1(1);
		for(rg int i=1;i<=n;i++)cout<<ans[i]<<" ";
	}else {
		DFS_Ring(1,1);/*
		for(rg int i=1;i<=n;i++){
			printf("rin[%d]=%d\n",i,rin[i]);
		}*/
		DFS2(1);
		for(rg int i=1;i<=n;i++)cout<<ans[i]<<" ";
	}
	return 0;
}

4.完結撒花~

繼續咕咕咕