ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2022-03-04 14:01:35  阅读:234  来源: 互联网

标签:Sort AtCoder return Beginner int ll 交换 const mod


F - Swap and Sort

题目大意:

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

思路:

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

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

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

具体实现上,我们可以使用并查集维护连通关系,每次交换暴力找到要交换的双方,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;
}

标签:Sort,AtCoder,return,Beginner,int,ll,交换,const,mod
来源: https://www.cnblogs.com/Nepenthe8/p/15963990.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有