Bigraph Extension
题意:
有2n个点,n为偶数,n个点属于集合A,n个点属于集合B。起初在途中有m个无向边,边的两侧端点分别在两个集合里,任何两个边都没有公共交点。
现在你可以执行任意次操作:
在集合A,B中分别选一个点,这两个点没有直接的边相连,现在给这两个点相连
在操作之后,对于集合A中任意一个点,集合B中任意一个点,需要满足:
这两个点是联通的
这两个点的最长简单路径是严格大于n的
问最少的加边数量,并按照最小字典序输出连边方案
题解:
构造题,不过这个题的结论其实好猜,具体证明就麻烦些
其实就是将2n个点构造成环,现在已经有了m个点,最小加边数就是2n-m
我们先不考虑环,先考虑将所有点连通
然后就是考虑字典序的最小限制,那我们就从小到达枚举集合A中的点,再从小到大枚举B中的点,通过维护并查集和度数数组来判断两个点是否连成链。这样就保证前2n-m-1条边的字典序最小。现在所有点已经联通了,不过还缺一个边,我们需要再加入一个边形成环,我们遍历A,B中度数为1的点连起来,放在第2n-m条边的位置
官方题解的详细证明:
代码:
#pragma GCC diagnostic error "-std=c++11"
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <unordered_map>
#define iss ios::sync_with_stdio(false)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef pair<int, int> pii;
const int mod = 1e9 + 7;
const int MAXN = 2e5 + 5;
const int inf = 0x3f3f3f3f;
int fa[MAXN];
int in[MAXN];
vector<pii> ans;
priority_queue<int, vector<int>, greater<int>> q;//最小堆
void init(int n)
{for (int i = 1; i <= 2 * n; i++) {fa[i] = i;in[i] = 0;}
}
int find(int x)
{if (fa[x] == x)return x;elsereturn fa[x] = find(fa[x]);
}
void combine(int u, int v)
{u = find(u);v = find(v);fa[u] = v;
}
int main()
{int t;scanf("%d", &t);while (t--) {ans.clear();int n, m;scanf("%d%d", &n, &m);init(n);for (int i = 1; i <= m; i++) {int u, v;scanf("%d%d", &u, &v);v += n;combine(u, v);in[u]++;in[v]++;}for (int i = n + 1; i <= 2 * n; i++)q.push(i);for (int i = 1; i <= n; i++) {queue<int> st;while (in[i] < 2 && !q.empty()) {int j = q.top();q.pop();if (find(j) != find(i)) {combine(i, j);in[i]++;in[j]++;ans.push_back({ i, j });}st.push(j);}while (!st.empty()) {int j = st.front();st.pop();if (in[j] < 2)q.push(j);}}int flag = 0, p1 = 0, p2 = 0;for (int i = 1; i <= n; i++) {if (in[i] == 1){p1 = i;break;}}for (int i = n + 1; i <= 2 * n; i++) {if (in[i] == 1){p2 = i;break;}}if(p1!=0&&p2!=0) {ans.push_back({ p1, p2 });
// printf("组成环:p1=%d,p2=%d\n",p1,p2-n); }if (flag) {printf("-1\n");} else {printf("%d\n", ans.size());for (auto i : ans) {printf("%d %d\n", i.first, i.second - n);}}while (!q.empty())q.pop();}
}