題解 P5022 【旅行】

小 Y 是一個愛好旅行的 OIer。她來到 X 國,打算將各個城市都玩一遍。

小Y瞭解到, X國的 nn 個城市之間有 mm 條雙向道路。每條雙向道路連接兩個城市。 不存在兩條連接同一對城市的道路,也不存在一條連接一個城市和它本身的道路。並且, 從任意一個城市出發,通過這些道路都可以到達任意一個其他城市。小 Y 只能通過這些 道路從一個城市前往另一個城市。

小 Y 的旅行方案是這樣的:任意選定一個城市作爲起點,然後從起點開始,每次可 以選擇一條與當前城市相連的道路,走向一個沒有去過的城市,或者沿着第一次訪問該 城市時經過的道路後退到上一個城市。當小 Y 回到起點時,她可以選擇結束這次旅行或 繼續旅行。需要注意的是,小 Y 要求在旅行方案中,每個城市都被訪問到。

爲了讓自己的旅行更有意義,小 Y 決定在每到達一個新的城市(包括起點)時,將 它的編號記錄下來。她知道這樣會形成一個長度爲 nn 的序列。她希望這個序列的字典序 最小,你能幫幫她嗎? 對於兩個長度均爲 nn 的序列 AABB,當且僅當存在一個正整數 xx,滿足以下條件時, 我們說序列 AA 的字典序小於 BB

  • 對於任意正整數 1i<x1 ≤ i < x,序列 AA 的第 ii 個元素 AiA_i 和序列 BB 的第 ii 個元素 BiB_i 相同。

  • 序列 AA 的第 xx 個元素的值小於序列 BB 的第 xx 個元素的值。

前置知識:

這道題數據範圍關鍵

首先,對於一棵樹,我們想求出字典序最優的方案肯定要每部都要儘量優。

比如:

這麼一棵樹,我們爲了讓字典序最優就得做些操作。

變成這個樣子:

360截圖18750814346171.png

我們把它轉換成代碼:

for(int i=n;i>=1;i--)sort(v[i].begin(),v[i].end());

求出字典序最優的方案自然也是水到渠成了

void dfs(int x,int fa,int y,int z){
	s.push_back(x);
	for(auto i:v[x])
		if(i!=fa&&(i!=y||x!=z)&&(i!=z||x!=y))dfs(i,x,y,z);
}

那麼對於基環樹怎麼辦呢?

比如這張圖:

360截圖18750814346171.png

怎麼辦呢?

我們發現肯定有一條邊是費的,再仔細想想,那條邊一定在換上面。

Y2lYpd.png

藍色線條上的就是環,我們怎麼去找環呢,可以使用 tarjan\texttt{tarjan} 算法 去找環。

找環的話先得建圖

圖的話,我們可以進行一遍搜索

void work(int x,int fa){
	if(lk[x])return;
	lk[x]=true;
	for(auto i:v[x])
		if(i!=fa){
			E[x].push_back(i);
			work(i,x);
		}
}

這樣圖就建好了

tarjan\texttt{tarjan} 的代碼我也放一下

void tarjan(int now){
	dfn[now]=low[now]=++dfscnt;
	sk[stop++]=now;
	for (auto i:E[now]){
		if(!dfn[i]){
			tarjan(i);
			low[now]=min(low[now],low[i]);
		}else if(!sccnum[i]){
			low[now]=min(low[now],dfn[i]);
		}
	}
	if(dfn[now]==low[now]){
		scccnt++;
		do{
			sccnum[sk[--stop]]=scccnt;
		}while(sk[stop]!=now);
	}
}

我們很容易地發現,環上的邊的兩個端點的 sccnumsccnum 是相同的。

我們再去看看圖

Y21ZE8.png

框出來的是 77 個強聯通分量,我們發現除了部分的強聯通分量大小超過 11,其他強聯通分量的大小都 =1= 1

所以,一條邊的 22 個端點的 sccnumsccnum 相同就表明這條邊一定是在鏈上面

代碼:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
	T RR=1;FF=0;char CH=getchar();
	for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
	for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
	FF*=RR;
}
int n,m,x[5010],y[5010];
vector<int>s,ans,v[5010],E[5010];
void dfs(int x,int fa,int y,int z){
	s.push_back(x);
	for(auto i:v[x])
		if(i!=fa&&(i!=y||x!=z)&&(i!=z||x!=y))dfs(i,x,y,z);
}
int sk[5010],stop,dfn[5010],low[5010],scccnt,sccnum[5010],dfscnt,lk[5010];
void tarjan(int now){
	dfn[now]=low[now]=++dfscnt;
	sk[stop++]=now;
	for (auto i:E[now]){
		if(!dfn[i]){
			tarjan(i);
			low[now]=min(low[now],low[i]);
		}else if(!sccnum[i]){
			low[now]=min(low[now],dfn[i]);
		}
	}
	if(dfn[now]==low[now]){
		scccnt++;
		do{
			sccnum[sk[--stop]]=scccnt;
		}while(sk[stop]!=now);
	}
}
void work(int x,int fa){
	if(lk[x])return;
	lk[x]=true;
	for(auto i:v[x])
		if(i!=fa){
			E[x].push_back(i);
			work(i,x);
		}
}
int main(){
	read(n);read(m);
	for(int i=1;i<=m;i++){
		read(x[i]);read(y[i]);
		v[x[i]].push_back(y[i]);
		v[y[i]].push_back(x[i]);
	}
	for(int i=n;i>=1;i--)ans.push_back(i),sort(v[i].begin(),v[i].end());
	work(1,0);
	for(int i=1;i<=n;i++)
		if(!dfn[i])tarjan(i);
	if(m==n-1){
		dfs(1,0,0,0);
		ans=min(ans,s);
	}else{
		for(int i=1;i<=m;i++)	
			if(sccnum[x[i]]==sccnum[y[i]]){
				s.clear();
				dfs(1,0,x[i],y[i]);
				ans=min(ans,s);
			}
	}
	for(auto i:ans)cout<<i<<" ";
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章