【LOJ3272】「JOISC 2020 Day1」汉堡肉

题目链接

点击打开链接

题目解法

考虑问题在一维上的形式,显然,我们会希望所选的最靠左侧的点尽量靠右。
因此,选择 min { R i } \min\{R_i\} 是不劣的,我们可以通过重复选择 min { R i } \min\{R_i\} 达成目标。

考虑在原问题中,一个确定的方案 ( x 1 , y 1 ) , ( x 2 , y 2 ) , (x_1,y_1),(x_2,y_2),\dots
若存在某个点 ( x i , y i ) (x_i,y_i) ,使得其余所有点均在其左下 / 左上 / 右下 / 右上方,那么将其改写为
( min { R i } , min { U i } ) , ( min { R i } , max { D i } ) , ( max { L i } , min { U i } ) , ( max { L i } , max { D i } ) (\min\{R_i\},\min\{U_i\}),(\min\{R_i\},\max\{D_i\}),(\max\{L_i\},\min\{U_i\}),(\max\{L_i\},\max\{D_i\})
四着中的某一者不会使得答案变得不合法。

因此,考虑如下算法:进行搜索,每次选择
( min { R i } , min { U i } ) , ( min { R i } , max { D i } ) , ( max { L i } , min { U i } ) , ( max { L i } , max { D i } ) (\min\{R_i\},\min\{U_i\}),(\min\{R_i\},\max\{D_i\}),(\max\{L_i\},\min\{U_i\}),(\max\{L_i\},\max\{D_i\})
中的一者加入答案,并删去被覆盖到的矩形。

其时间复杂度为 O ( 4 K × N ) O(4^K\times N) ,并且对于 K 3 K\leq 3 的数据是正确的。

对于 K = 4 K=4 的情况,以上算法无法找到的唯一一种解的形式是:
( x 0 , min { U i } ) , ( min { R i } , y 0 ) , ( x 1 , max { D i } ) , ( max { L i } , y 1 ) (x_0,\min\{U_i\}),(\min\{R_i\},y_0),(x_1,\max\{D_i\}),(\max\{L_i\},y_1)
min { R i } x 0 , x 1 max { L i } , min { U i } y 0 , y 1 max { D i } \min\{R_i\}\leq x_0,x_1\leq \max\{L_i\},\min\{U_i\}\leq y_0,y_1\leq \max\{D_i\}

由于题面保证了有解,若一个矩形若覆盖了某个点的取值范围,可以将其删去。
否则,一个矩形至多与某两个点的取值范围有交,即一个矩形可以写作有关某两个变量的限制。

对坐标离散化,考虑枚举 x 0 x_0
由于枚举完 x 0 x_0 后,问题变为了 K = 3 K=3 的形式,必然存在一个点取到删去若干矩形后的
( min { R i } , min { U i } ) , ( min { R i } , max { D i } ) , ( max { L i } , min { U i } ) , ( max { L i } , max { D i } ) (\min\{R_i\},\min\{U_i\}),(\min\{R_i\},\max\{D_i\}),(\max\{L_i\},\min\{U_i\}),(\max\{L_i\},\max\{D_i\})
因此, y 0 , y 1 y_0,y_1 中至少存在一者是取到此时的 min { U i } \min\{U_i\} 的。
不妨令 y 0 y_0 取到 min { U i } \min\{U_i\} ,即不违反 x 0 , y 0 x_0,y_0 间限制的最大值。

此时,与 x 0 , y 0 x_0,y_0 相关的限制将限定 x 1 , y 1 x_1,y_1 的范围。
我们只需要最后满足关于 x 1 , y 1 x_1,y_1 的限制即可,由于这样的限制形如
x 1 a   o r   y 1 b x_1\geq a\ or\ y_1\geq b ,我们只需要分别求出 x 1 , y 1 x_1,y_1 可取的最大值,再判断是否与限制冲突。

那么,用线段树维护各个 x 0 , y 0 x_0,y_0 对应在 x 1 , y 1 x_1,y_1 上的限制即可。

时间复杂度 O ( 4 K × N + N L o g N ) O(4^K\times N+NLogN)

#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;
}
发布了813 篇原创文章 · 获赞 93 · 访问量 18万+

猜你喜欢

转载自blog.csdn.net/qq_39972971/article/details/105101000