CF1559D Mocha and Diana 题解

Codeforces
Luogu

Description.

给定两棵森林,节点编号都是 \([1,n]\)。
每次操作选出两个节点 \(x\) 和 \(y\),满足在两棵树上 \(x\) 号节点均不和 \(y\) 号联通,并把他们相连。
最大化操作次数,并构造。

Solution.

设第一片森林的连通块个数为 \(tot_1\),第二片为 \(tot_2\)。
那我们答案上界是 \(n-\max(tot_1,tot_2)\)。
我们发现下界也是,尝试证明。
考虑最终状态,考虑连通块数量多的一片森林,设这片为 \(A\),另一片为 \(B\)。
我们发现如果 \(A\) 中存在 \(x\) 和 \(y\) 不联通,而 \(B\) 中它们也不联通,那就可以对 \(x\) 和 \(y\) 操作。
所以我们说明了如果 \(A\) 中的不同连通块内两个数,他们在 \(B\) 中一定联通。
所以如果 \(A\) 中连通块数 \(\ge 2\),那我们发现 \(A\) 中两两联通。
如果 \(A\) 中连通块数 \(=1\),说明 \(B\) 中连通块数 \(\le 1\),也两两联通。
所以 D1 直接暴力枚举两个点即可。


没找到性质
然后考虑不暴力。
钦定一个观察点 \(x\),先找到所有可以和它连边的点,连掉。
如果两边都联通,那对面点没什么花样。
如果一边联通一边不联通,那找到所有这样的两组点(按哪边联通分类)。
然后尽量匹配即可。
因为 \(a\) 和 \(x\) 联通,\(x\) 和 \(b\) 不联通可以推出 \(a\) 和 \(b\) 不联通。

Coding.

点击查看颓疯代码
//是啊,你就是那只鬼了,所以被你碰到以后,就轮到我变成鬼了{{{
#include<bits/stdc++.h>
using namespace std;typedef long long ll;
template<typename T>inline void read(T &x)
{
	x=0;char c=getchar(),bz=0;
	for(;c<48||c>57;c=getchar()) if(!(c^45)) bz=1;
	for(;c>=48&&c<=57;c=getchar()) x=(x<<1)+(x<<3)+(c^48);
	bz?x=-x:x;
}/*}}}*/
int n,m1,m2,a[100005],ta,b[100005],tb;vector<pair<int,int> >rs;
struct dsu
{
	int fa[100005];
	inline void init(int n) {for(int i=1;i<=n;i++) fa[i]=i;}
	inline int getf(int x) {return fa[x]==x?x:fa[x]=getf(fa[x]);}
	inline void mrg(int x,int y) {x=getf(x),y=getf(y);if(x^y) fa[x]=y;}
	inline char chk(int x,int y) {return getf(x)!=getf(y);}
}A,B;
inline void ad(int x,int y) {rs.push_back(make_pair(x,y));}
inline void flush()
{
	printf("%d\n",(int)rs.size());
	for(auto x:rs) printf("%d %d\n",x.first,x.second);
}
int main()
{
	read(n),read(m1),read(m2),A.init(n),B.init(n);
	for(int i=1,x,y;i<=m1;i++) read(x),read(y),A.mrg(x,y);
	for(int i=1,x,y;i<=m2;i++) read(x),read(y),B.mrg(x,y);
	for(int i=2;i<=n;i++) if(A.chk(1,i)&&B.chk(1,i)) A.mrg(1,i),B.mrg(1,i),ad(1,i);
	for(int i=2;i<=n;i++) if(A.chk(1,i)) a[++ta]=i,A.mrg(1,i);
	for(int i=2;i<=n;i++) if(B.chk(1,i)) b[++tb]=i,B.mrg(1,i);
	for(int i=1;i<=ta&&i<=tb;i++) ad(a[i],b[i]);
	return flush(),0;
}
上一篇:洛谷 P3209 [HNOI2010] 平面图判定


下一篇:杀人游戏