题目链接
题目解法
考虑问题在一维上的形式,显然,我们会希望所选的最靠左侧的点尽量靠右。
因此,选择
是不劣的,我们可以通过重复选择
达成目标。
考虑在原问题中,一个确定的方案
。
若存在某个点
,使得其余所有点均在其左下 / 左上 / 右下 / 右上方,那么将其改写为
四着中的某一者不会使得答案变得不合法。
因此,考虑如下算法:进行搜索,每次选择
中的一者加入答案,并删去被覆盖到的矩形。
其时间复杂度为 ,并且对于 的数据是正确的。
对于
的情况,以上算法无法找到的唯一一种解的形式是:
且
由于题面保证了有解,若一个矩形若覆盖了某个点的取值范围,可以将其删去。
否则,一个矩形至多与某两个点的取值范围有交,即一个矩形可以写作有关某两个变量的限制。
对坐标离散化,考虑枚举
。
由于枚举完
后,问题变为了
的形式,必然存在一个点取到删去若干矩形后的
因此,
中至少存在一者是取到此时的
的。
不妨令
取到
,即不违反
间限制的最大值。
此时,与
相关的限制将限定
的范围。
我们只需要最后满足关于
的限制即可,由于这样的限制形如
,我们只需要分别求出
可取的最大值,再判断是否与限制冲突。
那么,用线段树维护各个 对应在 上的限制即可。
时间复杂度 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 4e5 + 5;
const int INF = 1e9 + 7;
typedef long long ll;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
int n, k; bool vis[MAXN];
pair <int, int> ans[5], x[MAXN], y[MAXN];
void dfs(int depth) {
int Maxl = 0, Minr = INF, Maxd = 0, Minu = INF;
for (int i = 1; i <= n; i++)
if (!vis[i]) {
chkmax(Maxl, x[i].first);
chkmax(Maxd, y[i].first);
chkmin(Minr, x[i].second);
chkmin(Minu, y[i].second);
}
if (Maxl == 0) {
for (int i = 1; i <= depth; i++)
printf("%d %d\n", 1, 1);
for (int i = depth + 1; i <= k; i++)
printf("%d %d\n", ans[i].first, ans[i].second);
exit(0);
}
if (depth == 0) return;
vector <int> used; pair <int, int> res;
ans[depth] = res = make_pair(Maxl, Maxd);
for (int i = 1; i <= n; i++)
if (!vis[i] && x[i].first <= res.first && x[i].second >= res.first
&& y[i].first <= res.second && y[i].second >= res.second) {
vis[i] = true;
used.push_back(i);
}
dfs(depth - 1);
for (auto x : used) vis[x] = false; used.clear();
ans[depth] = res = make_pair(Maxl, Minu);
for (int i = 1; i <= n; i++)
if (!vis[i] && x[i].first <= res.first && x[i].second >= res.first
&& y[i].first <= res.second && y[i].second >= res.second) {
vis[i] = true;
used.push_back(i);
}
dfs(depth - 1);
for (auto x : used) vis[x] = false; used.clear();
ans[depth] = res = make_pair(Minr, Maxd);
for (int i = 1; i <= n; i++)
if (!vis[i] && x[i].first <= res.first && x[i].second >= res.first
&& y[i].first <= res.second && y[i].second >= res.second) {
vis[i] = true;
used.push_back(i);
}
dfs(depth - 1);
for (auto x : used) vis[x] = false; used.clear();
ans[depth] = res = make_pair(Minr, Minu);
for (int i = 1; i <= n; i++)
if (!vis[i] && x[i].first <= res.first && x[i].second >= res.first
&& y[i].first <= res.second && y[i].second >= res.second) {
vis[i] = true;
used.push_back(i);
}
dfs(depth - 1);
for (auto x : used) vis[x] = false; used.clear();
}
struct Dis {
set <int> st; map <int, int> mp;
int L, R, tot, home[MAXN];
void insert(int x) {
st.insert(x);
}
int build(int l, int r) {
L = l, R = r;
for (auto i = st.lower_bound(l); i != st.end() && (*i) <= r; i++) {
home[++tot] = (*i);
mp[*i] = tot;
}
return tot;
}
int query(int x) {
if (x < L) return 1;
if (x > R) return tot;
auto i = st.lower_bound(x);
return mp[*i];
}
int restore(int x) {
return home[x];
}
} X, Y;
struct rect {
int xl, xr, yl, yr;
void inter(const rect &a) {
chkmax(xl, a.xl);
chkmin(xr, a.xr);
chkmax(yl, a.yl);
chkmin(yr, a.yr);
}
};
rect cipher() {return (rect) {0, INF, 0, INF}; };
rect operator + (const rect &a, const rect &b) {
rect ans;
ans.xl = max(a.xl, b.xl);
ans.xr = min(a.xr, b.xr);
ans.yl = max(a.yl, b.yl);
ans.yr = min(a.yr, b.yr);
return ans;
}
struct SegmentTree {
struct Node {
int lc, rc;
rect tag;
} a[MAXN * 2];
int n, root, size;
void build(int &root, int l, int r) {
root = ++size;
a[root].tag = cipher();
if (l == r) return;
int mid = (l + r) / 2;
build(a[root].lc, l, mid);
build(a[root].rc, mid + 1, r);
}
void init(int x) {
n = x;
root = size = 0;
build(root, 1, n);
}
void modify(int root, int l, int r, int ql, int qr, rect t) {
if (l == ql && r == qr) {
a[root].tag.inter(t);
return;
}
int mid = (l + r) / 2;
if (mid >= ql) modify(a[root].lc, l, mid, ql, min(mid, qr), t);
if (mid + 1 <= qr) modify(a[root].rc, mid + 1, r, max(mid + 1, ql), qr, t);
}
void modify(int l, int r, rect v) {
if (l > r) return;
modify(root, 1, n, l, r, v);
}
rect res;
void query(int root, int l, int r, int pos) {
res.inter(a[root].tag);
if (l == r) return;
int mid = (l + r) / 2;
if (mid >= pos) query(a[root].lc, l, mid, pos);
else query(a[root].rc, mid + 1, r, pos);
}
rect query(int pos) {
res = cipher();
query(root, 1, n, pos);
return res;
}
} LX, LY;
int cntx, cnty;
void output(int x, int y, bool rev) {
if (rev) x = cntx + 1 - x;
printf("%d %d\n", X.restore(x), Y.restore(y));
}
void work(bool rev) {
LX.init(cntx), LY.init(cnty);
LX.modify(1, cntx, (rect) {1, cntx, 1, cnty});
static int limit[MAXN], req[MAXN];
for (int i = 1; i <= cntx; i++)
limit[i] = cnty, req[i] = 1;
for (int i = 1; i <= n; i++) {
int l = x[i].first, r = x[i].second;
int d = y[i].first, u = y[i].second;
if (l == 1 && r == cntx) {
if (d == 1 || u == cnty) continue;
chkmin(limit[1], u);
LY.modify(1, d - 1, (rect) {0, INF, d, u});
LY.modify(u + 1, cnty, (rect) {0, INF, d, u});
} else if (d == 1 && u == cnty) {
if (l == 1 || r == cntx) continue;
LX.modify(1, l - 1, (rect) {l, r, 0, INF});
LX.modify(r + 1, cntx, (rect) {l, r, 0, INF});
} else if (l == 1 && d == 1) {
chkmin(limit[r + 1], u);
} else if (l == 1 && u == cnty) {
LY.modify(1, d - 1, (rect) {l, r, 0, INF});
} else if (r == cntx && d == 1) {
LX.modify(1, l - 1, (rect) {0, INF, d, u});
} else if (r == cntx && u == cnty) {
chkmax(req[l - 1], d);
} else if (l == 1) {
chkmin(limit[1], u);
LY.modify(1, d - 1, (rect) {INF, 0, INF, 0});
} else if (r == cntx) {
LX.modify(1, cntx, (rect) {0, INF, d, u});
} else if (d == 1) {
LX.modify(1, l - 1, (rect) {INF, 0, INF, 0});
LX.modify(r + 1, cntx, (rect) {INF, 0, INF, 0});
} else if (u == cnty) {
LX.modify(1, cntx, (rect) {l, r, 0, INF});
} else assert(false);
}
for (int i = 2; i <= cntx; i++)
chkmin(limit[i], limit[i - 1]);
for (int i = cntx - 1; i >= 1; i--)
chkmax(req[i], req[i + 1]);
for (int i = 1; i <= cntx; i++) {
rect res = LX.query(i) + LY.query(limit[i]);
if (res.xl > res.xr || res.yl > res.yr) continue;
if (res.yr >= req[res.xr]) {
output(i, 1, rev);
output(1, limit[i], rev);
output(res.xr, cnty, rev);
output(cntx, res.yr, rev);
exit(0);
}
}
}
int main() {
read(n), read(k);
for (int i = 1; i <= n; i++) {
read(x[i].first), read(y[i].first);
read(x[i].second), read(y[i].second);
}
dfs(k), assert(k == 4);
int Maxl = 0, Minr = INF, Maxd = 0, Minu = INF;
for (int i = 1; i <= n; i++) {
chkmax(Maxl, x[i].first);
chkmax(Maxd, y[i].first);
chkmin(Minr, x[i].second);
chkmin(Minu, y[i].second);
}
assert(Minr <= Maxl && Minu <= Maxd);
for (int i = 1; i <= n; i++) {
X.insert(x[i].first - 1), X.insert(x[i].second);
Y.insert(y[i].first - 1), Y.insert(y[i].second);
}
X.insert(Maxl);
Y.insert(Maxd);
cntx = X.build(Minr, Maxl);
cnty = Y.build(Minu, Maxd);
for (int i = 1; i <= n; i++) {
x[i].first = X.query(x[i].first), x[i].second = X.query(x[i].second);
y[i].first = Y.query(y[i].first), y[i].second = Y.query(y[i].second);
}
work(false);
for (int i = 1; i <= n; i++) {
x[i].first = cntx + 1 - x[i].first;
x[i].second = cntx + 1 - x[i].second;
swap(x[i].first, x[i].second);
}
work(true);
assert(false);
return 0;
}