CF 793 div2 E 題解

793div2 E

可上 CF 看本題解

建模不多說,你會把排列拆成若干個輪換,然後對於長爲 \(k\) 的輪換,會且僅會用 \(k-1\) 次交換(因爲題目保證用的次數是最少的)。

把這些交換抓出來建圖,會得到一個森林,你需要給每條邊定向,使得每棵樹的拓撲序都可以和原輪換循環同構。

考慮如果滿足一個 \(i\to p_i\) 的置換,那麼你操作的順序一定是在樹上從 \(i\) 開始走然後走到 \(p_i\),這樣的路徑是唯一的。設經過了 \(m\) 條邊分別是 \(e_1,e_2,\cdots,e_m\),這 \(m\) 條邊會有嚴格拓撲序 \(\mathrm{opt}_{e_1}<\mathrm{opt}_{e_2}<\cdots<\mathrm{opt}_{e_m}\)

而這 \(m\) 條邊有這些拓撲序恰是滿足 \(i\to p_i\) 置換的充要條件。

充分:按這個拓撲序 \(i\) 可以走到 \(p_i\)

必要:樹上路徑唯一。

所以對於每個 \(i\to p_i\),在樹上抓出這些路徑然後加上有向邊,最後拓撲排序即可。

這樣做是對的,因爲一條邊是交換兩個數字,最多隻會影響兩個置換,所以會且恰好會被兩條路徑覆蓋。

總時間複雜度是 \(O(n)\) 的。

關於怎麼抓出路徑,無根轉有根後暴力跳 \(\mathrm{lca}\) 即可。

貼個代碼以免有人說我在口胡

// Problem: E. Unordered Swaps
// From: Codeforces - Codeforces Round #793 (Div. 2)
// URL: https://codeforces.com/contest/1682/problem/E
// Time: 2022-05-22 22:36
// Author: lingfunny

#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int mxn = 2e5+10;

int n, m, a[mxn], p[mxn], dep[mxn], in[mxn];

typedef pair<int, int> edge;
vector <edge> G[mxn];
edge fa[mxn];
vector <int> nodes, g[mxn];

inline void adde(int u, int v) { ++in[v]; g[u].push_back(v); }

void dfs(int u, int f) {
	dep[u] = dep[f] + 1; nodes.push_back(u);
	for(auto [id, v]: G[u]) if(v != f) fa[v] = {id, u}, dfs(v, u);
}

inline void solve(int u) {
	vector <int>().swap(nodes);
	dfs(u, 0);
	for(int x: nodes) {
		int y = p[x];	// x -> y
		vector <int> fx, fy;
		while(dep[x] > dep[y]) fx.push_back(fa[x].first), x = fa[x].second;
		while(dep[y] > dep[x]) fy.push_back(fa[y].first), y = fa[y].second;
		while(x != y) {
			fx.push_back(fa[x].first), x = fa[x].second;
			fy.push_back(fa[y].first), y = fa[y].second;
		}
		reverse(fy.begin(), fy.end());
		fx.insert(fx.end(), fy.begin(), fy.end());
		for(int i = 1; i < (int)fx.size(); ++i)
		adde(fx[i-1], fx[i]);
	}
}

signed main() {
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; ++i) scanf("%d", p+i);
	for(int i = 1; i <= m; ++i) {
		int x, y; scanf("%d%d", &x, &y);
		G[x].push_back({i, y});
		G[y].push_back({i, x});
	}
	for(int i = 1; i <= n; ++i) if(!dep[i]) solve(i);
	queue <int> q;
	for(int i = 1; i <= m; ++i) if(!in[i]) q.push(i);
	while(q.size()) {
		int u = q.front(); q.pop();
		printf("%d ", u);
		for(int v: g[u]) if(--in[v] == 0) q.push(v);
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章