【LOJ3272】「JOISC 2020 Day1」漢堡肉

題目鏈接

點擊打開鏈接

題目解法

考慮問題在一維上的形式,顯然,我們會希望所選的最靠左側的點儘量靠右。
因此,選擇 min{Ri}\min\{R_i\} 是不劣的,我們可以通過重複選擇 min{Ri}\min\{R_i\} 達成目標。

考慮在原問題中,一個確定的方案 (x1,y1),(x2,y2),(x_1,y_1),(x_2,y_2),\dots
若存在某個點 (xi,yi)(x_i,y_i) ,使得其餘所有點均在其左下 / 左上 / 右下 / 右上方,那麼將其改寫爲
(min{Ri},min{Ui}),(min{Ri},max{Di}),(max{Li},min{Ui}),(max{Li},max{Di})(\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{Ri},min{Ui}),(min{Ri},max{Di}),(max{Li},min{Ui}),(max{Li},max{Di})(\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(4K×N)O(4^K\times N) ,並且對於 K3K\leq 3 的數據是正確的。

對於 K=4K=4 的情況,以上算法無法找到的唯一一種解的形式是:
(x0,min{Ui}),(min{Ri},y0),(x1,max{Di}),(max{Li},y1)(x_0,\min\{U_i\}),(\min\{R_i\},y_0),(x_1,\max\{D_i\}),(\max\{L_i\},y_1)
min{Ri}x0,x1max{Li},min{Ui}y0,y1max{Di}\min\{R_i\}\leq x_0,x_1\leq \max\{L_i\},\min\{U_i\}\leq y_0,y_1\leq \max\{D_i\}

由於題面保證了有解,若一個矩形若覆蓋了某個點的取值範圍,可以將其刪去。
否則,一個矩形至多與某兩個點的取值範圍有交,即一個矩形可以寫作有關某兩個變量的限制。

對座標離散化,考慮枚舉 x0x_0
由於枚舉完 x0x_0 後,問題變爲了 K=3K=3 的形式,必然存在一個點取到刪去若干矩形後的
(min{Ri},min{Ui}),(min{Ri},max{Di}),(max{Li},min{Ui}),(max{Li},max{Di})(\min\{R_i\},\min\{U_i\}),(\min\{R_i\},\max\{D_i\}),(\max\{L_i\},\min\{U_i\}),(\max\{L_i\},\max\{D_i\})
因此, y0,y1y_0,y_1 中至少存在一者是取到此時的 min{Ui}\min\{U_i\} 的。
不妨令 y0y_0 取到 min{Ui}\min\{U_i\} ,即不違反 x0,y0x_0,y_0 間限制的最大值。

此時,與 x0,y0x_0,y_0 相關的限制將限定 x1,y1x_1,y_1 的範圍。
我們只需要最後滿足關於 x1,y1x_1,y_1 的限制即可,由於這樣的限制形如
x1a or y1bx_1\geq a\ or\ y_1\geq b ,我們只需要分別求出 x1,y1x_1,y_1 可取的最大值,再判斷是否與限制衝突。

那麼,用線段樹維護各個 x0,y0x_0,y_0 對應在 x1,y1x_1,y_1 上的限制即可。

時間複雜度 O(4K×N+NLogN)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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章