AtCoder Beginner Contest 233 F - Swap and Sort(并查集、思维)

F - Swap and Sort

题目大意:

给出一个 permutation,并给出 m 组关系 (ai,bi) ,每次操作可以交换 Pai,Pbi,问能否在 5e5 次操作之内将 permutation 变为升序序列,若能则输出交换次数和交换步骤。

思路:

若不需要输出交换步骤,这道题就很简单了。

我们可以发现,将交换关系看作一条无向边的话,处于同一个连通块内的数字可以任意交换,那么连通块内部一定能构成有序序列。这样一来,我们可以用优先队列维护每一个连通块的最小值,依次填上去看是否能构成升序序列即可。

在本题中需要输出交换步骤。注意到 n 的范围很小,那么考虑类似冒泡排序的交换,在每一轮交换中找度为 1 的点(下标)需要放的数在哪(下标)。为什么可以这样交换呢?因为每一轮交换都能够让度为1的点(下标)放在升序序列中该放的数,放完后不会参与和影响后面的操作,即无后效性。这样暴力的交换最坏情况即为降序序列,交换次数为 999+998++1=499500,满足题目限制。

具体实现上,我们可以使用并查集维护连通关系,每次交换暴力找到要交换的双方,dfs 去找交换路径即可。

Code:
Copy
#include <bits/stdc++.h> using namespace std; using ll = long long; const double eps = 1e-6; const int N = 1e6 + 7; //#define N 10 const int INF = 0x3f3f3f3f; const int mod = 1000000007; //998244353 const int dir[8][2] = {0, 1, 0, -1, 1, 0, -1, 0,/* dir4 */ -1, -1, -1, 1, 1, -1, 1, 1}; ll gcd(ll a, ll b) { return b == 0 ? a : gcd(b, a % b); } ll powmod(ll a, ll b) { ll res = 1; a %= mod; assert(b >= 0); for (; b; b >>= 1) { if (b & 1) res = res * a % mod; a = a * a % mod; } return res; } template <class T> bool ckmin(T &a, const T &b) { return b < a ? a = b, 1 : 0; } template <class T> bool ckmax(T &a, const T &b) { return a < b ? a = b, 1 : 0; } #define debug(a) cerr << "## " << #a << " = " << a << endl; class Dsu { public: static const int MAXN = 1e4 + 7; int fa[MAXN], rk[MAXN]; void init(int n) { for (int i = 1; i <= n; i++) { fa[i] = i, rk[i] = 1; } } int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); } bool merge(int x, int y) { x = find(x), y = find(y); if (x == y) { return 0; } if (rk[x] >= rk[y]) { fa[y] = x; } else { fa[x] = y; } if (rk[x] == rk[y] && x != y) rk[x]++; return 1; } bool isSame(int x, int y) { return find(x) == find(y); } } dsu; int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n; cin >> n; vector<int> p(n + 1); dsu.init(n); for (int i = 1; i <= n; i++) { cin >> p[i]; } int m; cin >> m; vector<vector<pair<int, int>>> g(n + 1); vector<int> deg(n + 1); for (int i = 0; i < m; i++) { int a, b; cin >> a >> b; if (dsu.merge(a, b)) { g[a].emplace_back(b, i + 1); g[b].emplace_back(a, i + 1); deg[a]++; deg[b]++; } } vector<int> path; function<bool(int, int, int)> dfs = [&](int u, int fa, int target) { if (u == target) { return true; } for (auto [v, id] : g[u]) { if (v == fa) { continue; } if (dfs(v, u, target)) { path.emplace_back(id); swap(p[u], p[v]); return true; } } return false; }; for (int round = 1; round <= n; round++) { for (int i = 1; i <= n; i++) { if (deg[i] == 1) { int target = -1; //下标,找度为1的点需要放的数在哪(注意是permutation for (int j = 1; j <= n; j++) { if (p[j] == i) { target = j; break; } } if (target == -1 || dfs(i, -1, target) == false) { cout << -1 << "\n"; return 0; } deg[i]--; for (auto [v, id] : g[i]) { deg[v]--; } break; } } } cout << (int)path.size() << "\n"; for (int i = 0; i < (int)path.size(); i++) { cout << path[i] << " \n"[i == (int)path.size() - 1]; } return 0; }
posted @   Nepenthe8  阅读(131)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示