luogu p5049 旅行(資料加強版)

2021-10-19 04:20:03 字數 3714 閱讀 3048

傳送門

給定乙個無向連通圖,不能走已經經過的點,可以回溯,每到乙個新點記錄編號,求字典序最小的編號序列。

點數 \(n\) 和邊數 \(m\) 的關係:\(m \in \\)

\(1 \le n \le 5\times10^5\)

看完這題後沒啥思路……但一看 \(m \in \\),感覺到好像不太對。也就是這張圖只可能是乙個樹或者乙個基環樹?

那就分情況討論唄。

也就是樹。

直接從1號節點開始,從小到大遍歷出邊的頂點進行dfs即可。

然後,就沒有然後了……

這個竟然佔了 \(60 \texttt\),划算到**(

基環樹。

基環樹就是樹連了一條邊,也就是樹中帶乙個環。

首先考慮將環上的所有節點標記上:

bool flcyc;

void dfscyc(int u, int fa)

dfscyc(v, u);

if (cyc[v] && flcyc) }}

}

然後可以想到,基環樹中一定有一條邊沒有走過。(並且這條邊在環上)

這個其實很好理解,基環樹上的環的邊如果都走過,那就不可能滿足 每個點除了第一次訪問或者回溯不能再次訪問 這一題目條件了。

那麼我們可以暴力刪邊跑 \(m = n - 1\),\(\mathcal(n^2)\)。這個能過弱化,但是本題資料顯然過不了,考慮在dfs上做手腳。

首先從 \(1\) 開始一直dfs,直到到達在環上的節點。

我們定義一次「反悔操作」為對於乙個節點,沒遍歷完所有子節點就回到上乙個節點的操作

顯然,\(m = n - 1\) 時不需要,也不能進行反悔操作(否則會有點到不了),但是 \(m = n\) 可以反悔次使得答案更優。(不能反悔兩次)

理論上講太晦澀,我們舉個例子。

這張圖如果按照正常dfs跑,答案是1 2 3 4 6 7 5

但是如果在3跑完4和6的時候,使用反悔**,退到2節點,然後繼續,答案就是1 2 3 4 6 5 7,顯然後者更優。

那麼現在問題來了,反悔只能用一次,該用在什麼時候呢?

首先,如果你還有不在環上的孩子沒走完,你能反悔嗎?不能。如果你現在反悔,那麼那些不在環上的孩子城市就永遠無法到達。

換句話說,所有不在環上的孩子走完,只剩乙個在環上的孩子,你才可以選擇反悔。

那剩下的問題就簡單了。現在只需要考慮只有乙個沒走完的孩子在環上的點能否反悔。

首先來看看反悔的本質能給我們帶來什麼好處吧。

我們把沒反悔之前本來要走的節點記為 \(p\),上乙個還有孩子沒走完的祖先的下乙個要走的孩子(其實就是反悔到的位置)記為 \(q\)。

比如上邊那張圖,本來我要遍歷到 \(p = 7\) 了,結果反悔到了上乙個還沒有走完孩子的祖先 \(2\),它下乙個要走的孩子是 \(q = 5\).

那麼字典序本來這個要填\(p = 7\),現在因為反悔要填 \(q = 5\) 了

字典序前面的遍歷序列已經確定,而字典序又是在前面的做主,那麼顯然決定字典序的只在於 \(p\) 和 \(q\) 的大小關係,哪個小對應的字典序就小。因此,如果 \(q < p\),就可以得到乙個更小的字典序,也就是說這次反悔划算。如果 \(q > p\),那就不划算了。

還有乙個問題:是早反悔好還是晚反悔好呢?

當然是早反悔好了!早反悔,可以把字典序越前面的數變小,那麼整個字典序顯然比晚反悔優

總結一下,當同時滿足以下三個條件時:

那就立刻反悔。

其他情況正常dfs即可,那麼 \(\mathtt\) 就這麼華麗麗的結束了。

暴力刪邊: \(\mathcal(n^2)\),較緊。

正解:\(\mathcal(n\log n)\)。

帶乙個 \(\log\) 是因為dfs的時候要從小到達選擇出邊點。有兩種解決方法:

不管哪種,複雜度都會帶乙個 \(\log\)。

ps:據說可以用一種類sa的基數排序思想使得 \(\log\) 降掉。整體時間複雜度可以降為 \(\mathcal(n)\)。不過常數較大……

/*

* @author: crab-in-the-northeast

* @date: 2020-11-28 10:37:32

* @last modified by: crab-in-the-northeast

* @last modified time: 2020-11-29 17:25:54

*/#include inline int read()

while (ch >= '0' && ch <= '9')

if (f)

return x;

return ~(x - 1);

}const int maxn = 500005;

const int maxm = 500005;

const int maxinf = 0x3f3f3f3f;

struct edges e[maxm << 1];

int head[maxn], ecnt;

int ans[maxn], cnt;

bool vis[maxn], cyc[maxn];

inline void insert(int u, int v) ;

head[u] = ecnt;

return ;

}bool flcyc;

void dfscyc(int u, int fa)

dfscyc(v, u);

if (cyc[v] && flcyc) }}

}bool fl;

void dfs(int u, int fa, int back)

while (!q.empty())

if (!vis[v])

dfs(v, u, (!q.empty() && cyc[u]) ? q.top() : back);

}}int main()

dfscyc(1, 1);

std :: memset(vis, 0, sizeof(vis));

dfs(1, 1, maxinf);

for (int i = 1; i <= cnt; ++i)

std :: printf("%d ", ans[i]);

puts("");

return 0;

}

基環樹找環這種基本操作一定要會,然後考場上別想複雜,大膽暴力 \(n ^ 2\) 是可以過樸素資料的。

類sa的基數排序優化就不寫了。因為常數挺大的,寫了並沒有什麼用(

luogu p1081 開車旅行

傳送門 此題為複雜細節題,無法總結題意,所以給出原題 小 text 和小 text 決定利用假期外出旅行,他們將想去的城市從 1 到 n 編號,且編號較小的城市在編號較大的城市的西邊,已知各個城市的海拔高度互不相同,記城市 i 的海拔高度為 h i 城市 i 和城市 j 之間的距離 d 恰好是這兩個...

luogu p1081 開車旅行

傳送門此題為複雜細節題,無法總結題意,所以給出原題 小 text 和小 text 決定利用假期外出旅行,他們將想去的城市從 1 到 n 編號,且編號較小的城市在編號較大的城市的西邊,已知各個城市的海拔高度互不相同,記城市 i 的海拔高度為 h i 城市 i 和城市 j 之間的距離 d 恰好是這兩個城...

Luogu P1137 旅行計畫

可以發現,因為只能往東邊走,並且有入度為 0 的起點,因此這是乙個有向無環圖,可以進行拓撲排序,求出拓撲序列。那麼我們要拓撲序列怎麼做呢?由於拓撲序列中,前面的點總是後面的點的前驅,因此可以進行dp。而dp的狀態轉移方程也很明顯,這個城市只能由前面的城市轉移過來,因此有方程 dis v max di...