「Codeforces 97B」Superset

Description

定义一个点集是“好的”,那么其中任意两点之间满足下列三个条件之一:

  • 横坐标相同
  • 纵坐标相同
  • 以这两个点为顶点的长方形包含(在内部或边缘)其他的至少一个点。

现在有 (n) 个点,找到一种添加点的方案,使得这个集合是“好的”。

构造其中一组总点数不超过 (2 imes 10^5) 的可行解。

Hint

  • (1le nle 10^4)
  • (| ext{坐标大小}| le 10^9)

Solution

平面分治

假如将点按 (x) 坐标排序,取中间点的 (x) 坐标作为中间线的 (x) 坐标,然后对所有点做这个中间线上的 投影,所形成的点就是要添加的点。加入这些点之后,可以发现中间线异侧的点对全都满足条件。

那同侧的呢?使用 踢皮球大法 递归处理。于是做好了。如果要判断重复点的话只要用一个 set 就好了。

Code

/*
 * Author : _Wallace_
 * Source : https://www.cnblogs.com/-Wallace-/
 * Problem : Codeforces 97B Superset 
 */
#include <algorithm>
#include <iostream>
#include <set>

using namespace std;
const int N = 2e5 + 5;

struct Point {
	int x, y;
	inline Point() { }
	inline Point(int _x, int _y) : x(_x), y(_y) { }
	inline bool operator < (const Point& t) const {
		return x != t.x ? x < t.x : y < t.y;
	}
} p[N];
set<Point> points;
int n;

void solve(int l, int r) {
	if (l == r) return;
	int mid = (l + r) >> 1, x = p[mid].x;
	solve(l, mid), solve(mid + 1, r);
	
	for (register int i = l; i <= r; i++)
		points.insert(Point(x, p[i].y));
}

signed main() {
	ios::sync_with_stdio(false);
	
	cin >> n;
	for (register int i = 1; i <= n; i++)
		cin >> p[i].x >> p[i].y;
	for (register int i = 1; i <= n; i++)
		points.insert(p[i]);
	
	sort(p + 1, p + 1 + n);
	solve(1, n);
	
	cout << points.size() << endl;
	for (set<Point>::iterator it = points.begin(); it != points.end(); it++)
		cout << it->x << ' ' << it->y << endl;
	return 0;
}