zoukankan      html  css  js  c++  java
  • CF1559D2

    题目

    Problem - D2

    给定两个不同的分别包含(n)个点的森林,每次可以同时在两个森林加一条相同的边,加边过程中不能出现环。问最多能加多少边,输出任一方案。(nle 10^5)

    题解

    在submission上看到一个很吊的做法。

    先确定一个根(rt),比如1号结点,然后遍历每个点(u),有三种情况:

    • (u)在两个森林中都和(rt)连了,那么不用再连;
    • (u)在两个森林中都不与(rt)相连,那么连上,添加答案((u,rt))
    • (u)在其中一个森林和(rt)相连,另一个不连,那么先插入一个栈保存起来。

    需要两个并查集和两个栈,上述操作完成后,两栈内结点再互相连接并加入答案。

    这个方法是正确的,因为第一次遍历操作完后,左边的森林中剩余的没和(rt)相连结点和右边的同样的结点交集为空。问题最优结果是其中一个森林变成树。这个算法可以保证最终一定有个森林最后会变成一个树且都是合法的。

    还有各种方法,这个方法虽然简单,但是确实难想到。

    #include <bits/stdc++.h>
     
    #define endl '
    '
    #define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
    #define mp make_pair
    #define seteps(N) fixed << setprecision(N)
    typedef long long ll;
     
    using namespace std;
    /*-----------------------------------------------------------------*/
     
    ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
    #define INF 0x3f3f3f3f
     
    const int N = 3e5 + 10;
    const double eps = 1e-5;
     
    int fa1[N];
    int fa2[N];
     
    int find(int x, int fa[]) {
        if(x == fa[x]) return fa[x];
        return fa[x] = find(fa[x], fa);
    }
     
    void merge(int u, int v, int fa[]) {
        int fu = find(u, fa), fv = find(v, fa);
        if(fu != fv) fa[fu] = fv;
    }
     
    typedef pair<int, int> PII;
    vector<PII> ans;
    stack<int> v1, v2;
     
    int main() {
        IOS;
        int n, m1, m2;
        cin >> n >> m1 >> m2;
        for(int i = 1; i <= n; i++) {
            fa1[i] = fa2[i] = i;
        }
        for(int i = 0; i < m1; i++) {
            int u, v;
            cin >> u >> v;
            merge(u, v, fa1);
        }
        for(int i = 0; i < m2; i++) {
            int u, v;
            cin >> u >> v;
            merge(u, v, fa2);
        }
        int rt = 1;
        for(int i = 1; i <= n; i++) {
            if((find(i, fa1) != find(rt, fa1)) && (find(i, fa2) != find(rt, fa2))) {
                ans.push_back({rt, i});
                merge(i, rt, fa1);
                merge(i, rt, fa2);
            }
        }
        for(int i = 1; i <= n; i++) {
            if((find(i, fa1) != find(rt, fa1)) && (find(i, fa2) == find(rt, fa2))) v1.push(i);
            if((find(i, fa1) == find(rt, fa1)) && (find(i, fa2) != find(rt, fa2))) v2.push(i);
        }
        while(!v1.empty() && !v2.empty()) {
            ans.push_back({v1.top(), v2.top()});
            merge(v1.top(), v2.top(), fa1);
            merge(v1.top(), v2.top(), fa2);
            while(!v1.empty() && (find(v1.top(), fa1) == find(rt, fa1))) v1.pop();
            while(!v2.empty() && (find(v2.top(), fa2) == find(rt, fa2))) v2.pop();
        }
        cout << ans.size() << endl;
        for(auto res : ans) cout << res.first << " " << res.second << endl;
    }
    
  • 相关阅读:
    sklearn 下的流行学习(Manifold Learning)—— sklearn.manifold
    errno与perror
    HDU 4638 Group 【树状数组,分块乱搞(莫队算法?)】
    android 客户端 和 新浪微博如何打通的
    HDU 4118 树形DP Holiday's Accommodation
    【jquery mobile笔记二】jquery mobile调用豆瓣api示例
    hdu4549之矩阵快速幂
    Cocos2d-iphone 为sprite添加双击的事件响应
    系统二级运维之业务单据错误处理
    我的北漂生活:我来北京一个月了
  • 原文地址:https://www.cnblogs.com/limil/p/15151681.html
Copyright © 2011-2022 走看看