【CF1559D2】Mocha and Diana (Hard Version)

题目

题目链接:https://codeforces.com/contest/1559/problem/D2
给你两棵森林,节点数均为 \(n\)。第一个森林有 \(m_1\) 条边,第二个森林有 \(m_2\) 条边。
允许你进行加边操作,但是有两个要求:

  • 如果在第一个森林加一条 \((u,v)\) 的边,第二个森林也要进行同样的操作。反之同理。
  • 加边后两个森林依旧是森林。(一棵树也是森林)

求最多能加几条边,并输出加边方案。
\(n,m_1,m_2\leq 10^5\)。(Easy Version:\(n,m_1,m_2\leq 1000\))。

思路

CF 题解讲的啥啊,洛谷题解好写易懂复杂度还低。
很显然最终至少有一个森林会被连成树。否则考虑两个森林中最大的连通块 \(S\),以及另一个不在连通块的点 \(x\),在另一个森林中,\(x\) 必然会连向 \(S\) 中所有点。与之前 \(S\) 是最大连通块矛盾。
所以可以直接枚举所有点对,如果这对点没有连通就连上。时间复杂度 \(O(n^2\alpha(n))\)。可以通过 Easy Version。
对于 Hard Version,首先把在两个森林中都不与 \(1\) 相连的点都连上 \(1\)。设此时第一个森林中没有与 \(1\) 相连的点集为 \(S\),第二个森林中没有与 \(1\) 相连的点集为 \(T\),显然 \(S\cap T=\emptyset\),所以这两个集合任意两个元素肯定不在一个连通块内。于是只要将 \(S\)\(T\) 中的点随意两两连边就好了。当其中一个集合空了后,就有一个森林是一棵树了。
时间复杂度 \(O(n\log n)\)

代码

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

const int N=100010;
int n,m1,m2,fa1[N],fa2[N];
queue<int> q1,q2;

int find(int *father,int x)
{
	return x==father[x]?x:father[x]=find(father,father[x]);
}

int main()
{
	scanf("%d%d%d",&n,&m1,&m2);
	for (int i=1;i<=n;i++)
		fa1[i]=fa2[i]=i;
	for (int i=1,x,y;i<=m1;i++)
	{
		scanf("%d%d",&x,&y);
		fa1[find(fa1,x)]=find(fa1,y);
	}
	for (int i=1,x,y;i<=m2;i++)
	{
		scanf("%d%d",&x,&y);
		fa2[find(fa2,x)]=find(fa2,y);
	}
	for (int i=2;i<=n;i++)
		if (find(fa1,i)!=find(fa1,1) && find(fa2,i)!=find(fa2,1))
		{
			q1.push(i); q2.push(1);
			fa1[find(fa1,i)]=find(fa1,1);
			fa2[find(fa2,i)]=find(fa2,1);
		}
	for (int i=2;i<=n;i++)
	{
		if (find(fa1,i)!=find(fa1,1))
			fa1[find(fa1,i)]=find(fa1,1),q1.push(i);
		if (find(fa2,i)!=find(fa2,1))
			fa2[find(fa2,i)]=find(fa2,1),q2.push(i);
	}
	cout<<min(q1.size(),q2.size())<<"\n";
	for (;q1.size() && q2.size();q1.pop(),q2.pop())
		cout<<q1.front()<<" "<<q2.front()<<"\n";
	return 0;
}
posted @ 2021-08-26 21:31  stoorz  阅读(54)  评论(0编辑  收藏  举报