CF1559D2 Mocha and Diana (Hard Version)

CF1559D2 Mocha and Diana (Hard Version)

思路

以下,两图分别称为 A , B

首先,来证明一个贪心策略:有能连的边就连,或者说连边不会影响最大值

考虑一种特殊情况,A 只有两个联通块,记为 x 和 y , B 只有两个联通块

如果从 x 中选出任意一点都无法连接到 y 中任意一点,那么说明 x 中任意一点和 y 中任意一点在 B 中在一个连通块内

这样推得 B 仅有唯一联通块,矛盾

所以上述情况一定可以连一条边

有一个显然的东西,连一条边会让 A 和 B 的联通块个数同时减 1 ,所以不难发现如果有 A 或 B 有一边成树了,那么一定没有可以连的边了

那么现在假设 A 有 r 个联通块, B 有 c 个联通块

不妨假设 A 和 B 中点 1 所在的联通块分别为 pa , pb

那么,将 A 中除去 pa 之外的视为一个整体, B 中同理,就转换为了上面的特殊情况

于是按上述可以一直连边直到一边成为一棵树,这显然也是能连的边数的最大值

那么贪心策略得证

考虑实现

首先,贪心将所有能和 1 连的点连接

那么现在图上只有三类点,在 A 和 B 与 1 联通,仅在 A 与 1 联通,仅在 B 与 1 联通

首先发现,第一类点没任何用,它不能任何点相连

然后显然的,一个第二类点可以和任意一个第三类点相连,因为他们不在同一个联通块

于是就可以贪心的连接了

只是要注意,连接可能会使后两类点变为第一类点,判断一下就好

代码

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef const int& cint;

const int mod = 1e9+7;
const int inf_int = 0x7fffffff;
const ll inf_ll = 0x7fffffffffffffff;
const double ept = 1e-9;

typedef pair<int,int> pii;
int n, m1, m2;
int bcj[100001][2];

int fd(cint x, cint st) {
    return bcj[x][st] == x ? x : bcj[x][st] = fd(bcj[x][st], st);
}

int main() {
    cin >> n >> m1 >> m2;
    int u, v;
    for(int i=1; i<=n; i++) bcj[i][0] = bcj[i][1] = i;
    for(int i=1; i<=m1; i++) {
        cin >> u >> v;
        u = fd(u, 0), v = fd(v, 0);
        if(u < v) swap(u, v);
        bcj[u][0] = v;
    }
    for(int i=1; i<=m2; i++) {
        cin >> u >> v;
        u = fd(u, 1), v = fd(v, 1);
        if(u < v) swap(u, v);
        bcj[u][1] = v;
    }
    vector<pii> e;
    for(int i=2; i<=n; i++) {
        int s1 = fd(i, 0), s2 = fd(i, 1);
        if(s1 != 1 && s2 != 1) {
            bcj[s1][0] = 1;
            bcj[s2][1] = 1;
            e.push_back( {1, i} );
        }
    }
    queue<int> r[2];
    for(int i=2; i<=n; i++) {
        int s1 = fd(i, 0), s2 = fd(i, 1);
        if(s1 == 1 && s2 != 1) r[0].push(i);
        if(s1 != 1 && s2 == 1) r[1].push(i);
    }
    while(!r[0].empty() && !r[1].empty()) {
        while(!r[0].empty() && fd(r[0].front(), 0) == 1 && fd(r[0].front(), 1) == 1) r[0].pop();
        while(!r[1].empty() && fd(r[1].front(), 0) == 1 && fd(r[1].front(), 1) == 1) r[1].pop();
        if(!r[0].empty() && !r[1].empty()) {
            int s1 = r[0].front(), s2 = r[1].front();
            r[0].pop(), r[1].pop();
            bcj[fd(s2, 0)][0] = 1;
            bcj[fd(s1, 1)][1] = 1;
            e.push_back( {s1, s2} );
        }
    }

    cout << e.size() << endl;
    for(auto k: e) {
        cout << k.first << ' ' << k.second << endl;
    }
    return 0;
}
posted @ 2021-08-17 21:57  ullio  阅读(63)  评论(0编辑  收藏  举报