CF1559D2 Mocha and Diana (Hard Version)

考虑把每个点看成一个二元组 ((A, B))(A)(B) 分别表示这个点在第一个和第二个森林中处在的联通块的标号。

把这些 ((A, B)) 画到坐标系上,发现有同一个纵坐标或同一个横坐标的两点无法连边。

(x_1(a_1, b_1))(x_2(a_2, b_2))((a_1 eq a_2 and b_1 eq b_2)),考虑在这两个点之间连边会发生什么。

  • (x_2) 的坐标变成了 (x_1) 的。
  • 坐标为 ((?, b_2)) 的节点都变成了 ((?, b_1))
  • 坐标为 ((a_2, ?)) 的节点都变成了 ((a_1, ?))

然后你想到将所有可以与 (1) 号节点连边的点全部连边,那么最后坐标系上的点只会在直线 (y = y_1)(x = x_1) 上存在。

把剩下的点分成两类,一类是 ((?, y_1)) 另一类是 ((x_1, ?)),分别按照 (?) 排序,最后将一类的第 (k) 小和另一类的第 (k) 小的点连边,然后你会发现这时坐标轴上其中一条线就会从左往右移动,最后越来越短,且每次操作都是合法的。

时间复杂度 (mathcal O (dsu + nlog n))

#include <bits/stdc++.h>
#define forn(i,s,t) for(register int i=(s); i<=(t); ++i)
#define form(i,s,t) for(register int i=(s); i>=(t); --i)
#define rep(i,s,t) for(register int i=(s); i<(t); ++i)
using namespace std;
const int N = 1e5 + 3;
struct dsu {
	int fa[N];
	int Fnd(int u) {return fa[u] == u ? u : fa[u] = Fnd(fa[u]);}
	inline void Mrg(int u, int v) {
		u = Fnd(u), v = Fnd(v);
		if(u == v) return ;
		fa[v] = u;
	}
} M, D;
int n, m1, m2, u, v, tot, resx[N], resy[N], r1[N], r2[N], tt1, tt2;
#define x(p) M.Fnd(p)
#define y(p) D.Fnd(p)
inline bool Xcmp(int A, int B) {return y(A) < y(B);}
inline bool Ycmp(int A, int B) {return x(A) < x(B);}
int main() {
	scanf("%d%d%d", &n, &m1, &m2);
	forn(i,1,n) M.fa[i] = D.fa[i] = i;
	forn(i,1,m1) scanf("%d%d", &u, &v), M.Mrg(u, v);
	forn(i,1,m2) scanf("%d%d", &u, &v), D.Mrg(u, v);
	forn(i,2,n) if(x(i) != x(1) && y(i) != y(1)) resx[++tot] = 1, resy[tot] = i, M.Mrg(1, i), D.Mrg(1, i);
	forn(i,1,n) {
		if(x(i) == x(1) && y(i) != y(1)) r1[++tt1] = i, D.Mrg(1, i);
		if(y(i) == y(1) && x(i) != x(1)) r2[++tt2] = i, M.Mrg(1, i);
	}
	sort(r1 + 1, r1 + tt1 + 1, Xcmp);
	sort(r2 + 1, r2 + tt2 + 1, Ycmp);
	forn(i,1,min(tt1, tt2)) resx[++tot] = r1[i], resy[tot] = r2[i];
	printf("%d
", tot);
	forn(i,1,tot) printf("%d %d
", resx[i], resy[i]);
	return 0;
}
原文地址:https://www.cnblogs.com/Ax-Dea/p/15233962.html