LOJ2818 eJOI2018 迴圈排序

2022-03-12 11:39:40 字數 3261 閱讀 3430

題目鏈結

約定:以下我們定義有向圖的「連通塊」,是指它的極大基圖連通子圖。其中基圖是指把有向邊邊視作無向邊,得到的無向圖。

首先要搞清楚,對於一種排序方案,有兩個引數來衡量它:操作次數,和操作到的位置數。本題裡,我們要在滿足【操作到的位置數】\(\leq s\) 的前提下,最小化【操作次數】。思考時,千萬不要將這兩個量搞混了。

先將 \(a\) 序列離散化。設最大值為 \(m\)。然後求出排好序的序列長什麼樣子,設為 \(b\)。

建一張 \(m\) 個點的圖。對每個 \(i\) (\(1\leq i\leq n\)),若 \(a_i\neq b_i\),我們連一條有向邊 \((b_i,a_i)\),原因稍後解釋。記這條邊的「編號」為 \(i\)。

容易發現,每個 \(a_i\neq b_i\) 的 \(i\) 至少要作為【操作到的位置】出現一次。設這樣的 \(i\) 有 \(k\) 個,則【操作到的位置數】至少為 \(k\)。所以如果 \(k > s\),則可以直接判為無解。否則,發現【操作到的位置數】是可以等於 \(k\) 的。考慮我們剛剛建出來的有向圖,這張圖里每個點入度一定等於出度。因此這張圖里的每個連通塊,都存在尤拉迴路。可以構造出一種操作方案是:對每個連通塊,求出它的尤拉迴路,按順序輸出所有邊。此時讀者不難發現,根據我們的建圖方式,把乙個連通塊的尤拉回路上每條邊的編號按順序操作一遍後,就能使連通塊裡所有數值走到它最終應該走到的位置。

特別地,如果 \(a\) 是乙個排列,則我們建出的有向圖實際就是若干個環。每個點入度、出度都為 \(1\)。這是入度等於出度的一種特殊情況。應該可以幫助讀者更好地理解上述操作策略。

設有 \(c\) 個連通塊。那麼在上述的操作策略中,我們所用的【操作次數】為 \(c\),【操作到的位置數】為 \(k\)。前面已經說明過,這是能將【操作到的位置數】最小化的方案。但如果在允許的範圍內(\(\leq s\)),把【操作到的位置數】擴大一些,可能能進一步減小【操作次數】。

具體來說,我們可以用 \(1\) 次操作,將若干個連通塊合併為乙個連通塊,使新連通塊仍然存在尤拉迴路。具體的方法是在每個連通塊裡任選一條邊,設選出的邊編號分別為 \(i_1,i_2,\dots,i_t\),那麼對 \(i_1,i_2,\dots ,i_t\) 做一次操作,就能使這些連通塊以乙個環的形式串起來。然後我們只需要再對新連通塊做 \(1\) 次操作,就能使裡面的數全部走到最終位置!

換句話說,無論多少個連通塊,我們只需要 \(2\) 次操作,就能將它們全部排好序。但同時,這會使得【操作到的位置數】增大相應的連通塊數量。所以有可能我們不能對所有連通塊進行操作。具體來說,我們只會任選 \(x = \min(c, s - k)\) 個連通塊,將它們合併。最終,若 \(x < 2\),則【操作次數】就是 \(c\)(不搞合併了),否則是:\(2 + (c - x)\)。

還有乙個小問題,就是求有向圖的尤拉迴路。我用 \(\text\) 來實現。那麼對於有向邊 \((u,v)\),只能在從 \(v\) 回溯時輸出(或記錄)這條邊,而不能在進入 \(v\) 之前輸出,否則輸出的可能就不是一條連續、完整的路徑了。但是這樣記錄下的路徑,與我們實際走的順序其實是反的。所以你可以把路徑 \(\text\) 一下,或者在建邊的時候就乾脆建反向邊。

時間複雜度 \(o(n\log n)\)。因為要排序。

在 loj 檢視

// problem: loj2818

#include using namespace std;

#define pb push_back

#define mk make_pair

#define lob lower_bound

#define upb upper_bound

#define fi first

#define se second

#define sz(x) ((int)(x).size())

typedef unsigned int uint;

typedef long long ll;

typedef unsigned long long ull;

typedef pairpii;

templateinline void ckmax(t& x, t y)

templateinline void ckmin(t& x, t y)

const int maxn = 2e5;

int n, lim, a[maxn + 5], goal[maxn + 5];

int vals[maxn + 5], cnt_val;

int need_oper_pos;

vector> ans;

int fa[maxn + 5], sz[maxn + 5];

int last_edge[maxn + 5];

int get_fa(int u)

void unite(int u, int v, int e)

last_edge[fu] = e;

}struct edge edge[maxn + 5];

int head[maxn + 5], tot;

inline void add_edge(int u, int v, int eid)

vectorpos;

void dfs(int u)

}int main()

sort(vals + 1, vals + cnt_val + 1);

for (int i = 1; i <= n; ++i) goal[i] = vals[i]; // 目標序列

cnt_val = unique(vals + 1, vals + cnt_val + 1) - (vals + 1);

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

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

} if (need_oper_pos > lim)

int rest = lim - need_oper_pos;

// cerr << rest << endl;

if (rest >= 2)

} if (sz(pos) >= 2)

a[pos[0]] = last_val;

} }for (int i = 1; i <= n; ++i)

} for (int i = 1; i <= n; ++i)

} cout << sz(ans) << endl;

for (int i = 0; i < sz(ans); ++i)

} return 0;

}

eJOI2018 元素週期表

題目 r 1,c 1 r 2,c 1 r 1,c 2 三個格仔存在就說明 r 2,c 2 存在,如果我們將 r 1,c 2,c 1,r 2 都看成一些點的話,那麼這個關係就非常類似於 r 1 和 c 1 聯通,r 2 和 c 1 聯通,c 2 和 r 1 聯通,那麼就說明 r 2 和 c 2 聯通 ...

LOJ2542 PKUWC2018 隨機遊走

分析 sum limits 1 min sum limits na i sum limits 1 j binom sum limits 1 min sum limits na i n i 0 sum limits 1 min a n max 設 f 表示 f 第一次走到 s 狀態的期望步數。這個東西...

LOJ6388 THUPC2018 賽艇 題解

如果你做過bzoj5217 lydsy2017省隊十連測 航海艦隊的話,那麼恭喜你,這道題就是大水題。如果你做過bzoj4259 殘缺的字串的話,那麼基本的字串匹配fft也是能想到的。如果沒做過的話,很抱歉,沒有一定的套路的話這道題很難想 而對於我這樣的蒟蒻來說就是沒法想。將行走路線看做乙個地圖b,...