小 Y 是一個愛好旅行的 OIer。她來到 X 國,打算將各個城市都玩一遍。
小Y瞭解到, X國的 個城市之間有 條雙向道路。每條雙向道路連接兩個城市。 不存在兩條連接同一對城市的道路,也不存在一條連接一個城市和它本身的道路。並且, 從任意一個城市出發,通過這些道路都可以到達任意一個其他城市。小 Y 只能通過這些 道路從一個城市前往另一個城市。
小 Y 的旅行方案是這樣的:任意選定一個城市作爲起點,然後從起點開始,每次可 以選擇一條與當前城市相連的道路,走向一個沒有去過的城市,或者沿着第一次訪問該 城市時經過的道路後退到上一個城市。當小 Y 回到起點時,她可以選擇結束這次旅行或 繼續旅行。需要注意的是,小 Y 要求在旅行方案中,每個城市都被訪問到。
爲了讓自己的旅行更有意義,小 Y 決定在每到達一個新的城市(包括起點)時,將 它的編號記錄下來。她知道這樣會形成一個長度爲 的序列。她希望這個序列的字典序 最小,你能幫幫她嗎? 對於兩個長度均爲 的序列 和 ,當且僅當存在一個正整數 ,滿足以下條件時, 我們說序列 的字典序小於 。
對於任意正整數 ,序列 的第 個元素 和序列 的第 個元素 相同。
序列 的第 個元素的值小於序列 的第 個元素的值。
前置知識:
這道題數據範圍是關鍵。
首先,對於一棵樹,我們想求出字典序最優的方案肯定要每部都要儘量優。
比如:
這麼一棵樹,我們爲了讓字典序最優就得做些操作。
變成這個樣子:
我們把它轉換成代碼:
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);
}
那麼對於基環樹怎麼辦呢?
比如這張圖:
怎麼辦呢?
我們發現肯定有一條邊是費的,再仔細想想,那條邊一定在換上面。
藍色線條上的就是環,我們怎麼去找環呢,可以使用 算法 去找環。
找環的話先得建圖
圖的話,我們可以進行一遍搜索
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);
}
}
這樣圖就建好了
的代碼我也放一下
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);
}
}
我們很容易地發現,環上的邊的兩個端點的 是相同的。
我們再去看看圖
框出來的是 個強聯通分量,我們發現除了環部分的強聯通分量大小超過 ,其他強聯通分量的大小都 。
所以,一條邊的 個端點的 相同就表明這條邊一定是在鏈上面。
代碼:
#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;
}