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


    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;
    }
    
  • 相关阅读:
    华南师范大学应用数学考研真题
    scoped引起的deep与>>>改变elementUI的样式
    7.mogodb索引
    JS的undefined与null,==与===的区别
    vue上传图片到七牛云的思路与实现
    JS用正则处理文件名
    JS使用正则匹配字符串去掉多余符号
    6.mongoDB更新操作
    SSL证书相关之后缀名笔记
    vue一些常用的语法
  • 原文地址:https://www.cnblogs.com/Nepenthe8/p/15963990.html
Copyright © 2020-2023  润新知