AtCoder Beginner Contest 233 F - Swap and Sort(并查集、思维)
题目大意:
给出一个 permutation,并给出 组关系 ,每次操作可以交换 ,问能否在 5e5 次操作之内将 permutation 变为升序序列,若能则输出交换次数和交换步骤。
思路:
若不需要输出交换步骤,这道题就很简单了。
我们可以发现,将交换关系看作一条无向边的话,处于同一个连通块内的数字可以任意交换,那么连通块内部一定能构成有序序列。这样一来,我们可以用优先队列维护每一个连通块的最小值,依次填上去看是否能构成升序序列即可。
在本题中需要输出交换步骤。注意到 的范围很小,那么考虑类似冒泡排序的交换,在每一轮交换中找度为 1 的点(下标)需要放的数在哪(下标)。为什么可以这样交换呢?因为每一轮交换都能够让度为1的点(下标)放在升序序列中该放的数,放完后不会参与和影响后面的操作,即无后效性。这样暴力的交换最坏情况即为降序序列,交换次数为 ,满足题目限制。
具体实现上,我们可以使用并查集维护连通关系,每次交换暴力找到要交换的双方,dfs 去找交换路径即可。
Code:
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】