「JOISC 2020 Day1」汉堡肉

考虑一维的情况,我们选择的最左边那个点一定是所有线段的 \(\min\{r_i\}\),因为要在没有任何线段在它左边的情况下使得左端点尽量靠右。同理最右边那个点一定是所有线段的 \(\max\{l_i\}\)

因此枚举选这两个点中的哪个,删去与这个点有交的线段,递归深搜即可。

扩展到二维的情况,最上面那个点一定是 \((x_0,\max\{d_i\})\),最下面是 \((x_1,\min\{u_i\})\),最左边是 \((\min\{r_i\},x_2)\),最右边是 \((\max\{l_i\},x_3)\)

我们称 \(x\) 坐标 \(\min\{r_i\}\)\(\max\{l_i\}\) 以及 \(y\) 坐标 \(\max\{d_i\}\)\(\min\{u_i\}\) 的线段为关键线段。最后选择的点一定在四条关键线段构成的矩形上,且四条关键线段上一定有点被选择。

对于 \(k\le 3\) 的情况,三个点却要覆盖四条线段,显然需要选择交点。枚举四个选哪个,按上述方法深搜,复杂度 \(O(n4^k)\)

\(k=4\) 先深搜看看有没出解,如果没有再继续讨论。

对于每个矩形,如果与 \(3\) 条及以上关键线段相交,那么内部至少一个点,不用管。

如果只与 \(1\) 条相交,那么这条关键线段上的点必定落在与这条关键线段相交的区间内。

如果与 \(2\) 条相交,那么两条相交线段必有一处上有点,典型 2-SAT。

图中两条黄色括起来的线段至少有一个内部有点。

每条关键线段上的线段按右端点排序,二分找到第一个右端点比当前线段左端点还小的点,前缀优化建图即可。

代码及其好写,也就 10KB+嘛,比别人长了两三倍而已

#include <cstdio>
#include <vector>
#include <algorithm>
#define gc (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 65536, stdin), p1 == p2) ? EOF : *p1 ++)

inline int min(const int x, const int y) {return x < y ? x : y;}
inline int max(const int x, const int y) {return x > y ? x : y;}

char buf[65536], *p1, *p2;
inline int read() {
	char ch;
	int x = 0;
	while ((ch = gc) < 48);
	do x = x * 10 + ch - 48; while ((ch = gc) >= 48);
	return x;
}

int l[200005], r[200005], d[200005], u[200005], tl[200005], tr[200005], td[200005], tu[200005];
int l2[5][200005], r2[5][200005], d2[5][200005], u2[5][200005];
int a[5], b[5];
int n, k, left = 1e9, right, down = 1e9, up, now, cnt = 1;
std::vector<int> rect;

bool dfs(int step) {
	if (!now) {
		for (int i = 1; i < step; ++ i) printf("%d %d\n", a[i], b[i]);
		for (int i = step; i <= k; ++ i) puts("1 1");
		return true;
	}
	if (step > k) return false;
	int left = 2e9, right = 0, down = 2e9, up = 0;
	for (int i = 1; i <= now; ++ i) {
		right = max(right, l[i]);
		up = max(up, d[i]);
		left = min(left, r[i]);
		down = min(down, u[i]);
	}
	int tmp = 0, tnow = now;
	a[step] = left, b[step] = down;
	for (int i = 1; i <= now; ++ i)
		l2[step][i] = l[i], r2[step][i] = r[i], d2[step][i] = d[i], u2[step][i] = u[i];
	for (int i = 1; i <= now; ++ i)
		if (!(l[i] <= left && left <= r[i] && d[i] <= down && down <= u[i]))
			tl[++ tmp] = l[i], tr[tmp] = r[i], td[tmp] = d[i], tu[tmp] = u[i];
	now = tmp;
	for (int i = 1; i <= now; ++ i) l[i] = tl[i], r[i] = tr[i], d[i] = td[i], u[i] = tu[i];
	if (dfs(step + 1)) return true;
	
	now = tnow;
	for (int i = 1; i <= now; ++ i)
		l[i] = l2[step][i], r[i] = r2[step][i], d[i] = d2[step][i], u[i] = u2[step][i];
	a[step] = left, b[step] = up, tmp = 0;
	for (int i = 1; i <= now; ++ i)
		if (!(l[i] <= left && left <= r[i] && d[i] <= up && up <= u[i]))
			tl[++ tmp] = l[i], tr[tmp] = r[i], td[tmp] = d[i], tu[tmp] = u[i];
	now = tmp;
	for (int i = 1; i <= now; ++ i) l[i] = tl[i], r[i] = tr[i], d[i] = td[i], u[i] = tu[i];
	if (dfs(step + 1)) return true;
	
	now = tnow;
	for (int i = 1; i <= now; ++ i)
		l[i] = l2[step][i], r[i] = r2[step][i], d[i] = d2[step][i], u[i] = u2[step][i];
	a[step] = right, b[step] = down, tmp = 0;
	for (int i = 1; i <= now; ++ i)
		if (!(l[i] <= right && right <= r[i] && d[i] <= down && down <= u[i]))
			tl[++ tmp] = l[i], tr[tmp] = r[i], td[tmp] = d[i], tu[tmp] = u[i];
	now = tmp;
	for (int i = 1; i <= now; ++ i) l[i] = tl[i], r[i] = tr[i], d[i] = td[i], u[i] = tu[i];
	if (dfs(step + 1)) return true;
	
	now = tnow;
	for (int i = 1; i <= now; ++ i)
		l[i] = l2[step][i], r[i] = r2[step][i], d[i] = d2[step][i], u[i] = u2[step][i];
	a[step] = right, b[step] = up, tmp = 0;
	for (int i = 1; i <= now; ++ i)
		if (!(l[i] <= right && right <= r[i] && d[i] <= up && up <= u[i]))
			tl[++ tmp] = l[i], tr[tmp] = r[i], td[tmp] = d[i], tu[tmp] = u[i];
	now = tmp;
	for (int i = 1; i <= now; ++ i) l[i] = tl[i], r[i] = tr[i], d[i] = td[i], u[i] = tu[i];
	if (dfs(step + 1)) return true;
	
	now = tnow;
	for (int i = 1; i <= now; ++ i)
		l[i] = l2[step][i], r[i] = r2[step][i], d[i] = d2[step][i], u[i] = u2[step][i];
	return false;
}

namespace TwoSAT {
	struct Edge {
		int to, nxt;
	} e[10000005];
	const int N = 2000000;
	int head[N], s[N], dfn[N], low[N], col[N], tot, top, cnt, scc, n;
	bool Instack[N];
	inline void AddEdge(const int u, const int v) {
		e[++ tot].to = v, e[tot].nxt = head[u], head[u] = tot;
	}
	void Tarjan(int u) {
		dfn[u] = low[u] = ++ cnt, s[++ top] = u, Instack[u] = true;
		for (int i = head[u]; i; i = e[i].nxt) {
			int v = e[i].to;
			if (!dfn[v]) Tarjan(v), low[u] = min(low[u], low[v]);
			else if (Instack[v]) low[u] = min(low[u], dfn[v]);
		}
		if (dfn[u] == low[u]) {
			++ scc;
			do
				col[s[top]] = scc, Instack[s[top]] = false;
			while (s[top --] != u);
		}
	}
	std::vector<int> solve() {
		std::vector<int> result;
		for (int i = 2; i <= n; ++ i)
			if (!dfn[i]) Tarjan(i);
		for (int i = 2; i <= n; i += 2)
			result.push_back(col[i] < col[i ^ 1] ? i : i ^ 1);
		return result;
	}
};

struct Line {
	int l, r, x1, x2;
	inline bool operator < (const Line a) const {return r < a.r;}
	inline bool operator < (const int x) {return r < x;}
} L[200005], R[200005], D[200005], U[200005]; 
int prel1[200005], prel2[200005], prer1[200005], prer2[200005];
int pred1[200005], pred2[200005], preu1[200005], preu2[200005];
int type[1000005], lb[1000005], rb[1000005];
int cntl, cntr, cntd, cntu;
std::vector<int> ans;

int main() {
	now = n = read(), k = read();
	for (int i = 1; i <= n; ++ i) {
		right = max(right, l[i] = l2[0][i] = read());
		up = max(up, d[i] = d2[0][i] = read());
		left = min(left, r[i] = r2[0][i] = read());
		down = min(down, u[i] = u2[0][i] = read());
	}
	if (dfs(1)) return 0;
	for (int i = 1; i <= n; ++ i)
		l[i] = l2[0][i], r[i] = r2[0][i], d[i] = d2[0][i], u[i] = u2[0][i];
	
	for (int i = 1; i <= n; ++ i) {
		int num = 0;
		if (l[i] <= left && left <= r[i]) ++ num;
		if (l[i] <= right && right <= r[i]) ++ num;
		if (d[i] <= down && down <= u[i]) ++ num;
		if (d[i] <= up && up <= u[i]) ++ num;
		if (num >= 3) continue;
		if (num == 1) {
			if (l[i] <= left && left <= r[i]) {
				L[++ cntl] = Line{d[i], u[i], cnt + 1, cnt + 2};
				type[cnt + 1] = 1, lb[cnt + 1] = d[i], rb[cnt + 1] = u[i];
			}
			if (l[i] <= right && right <= r[i]) {
				R[++ cntr] = Line{d[i], u[i], cnt + 1, cnt + 2};
				type[cnt + 1] = 2, lb[cnt + 1] = d[i], rb[cnt + 1] = u[i];
			}
			if (d[i] <= down && down <= u[i]) {
				D[++ cntd] = Line{l[i], r[i], cnt + 1, cnt + 2};
				type[cnt + 1] = 3, lb[cnt + 1] = l[i], rb[cnt + 1] = r[i];
			}
			if (d[i] <= up && up <= u[i]) {
				U[++ cntu] = Line{l[i], r[i], cnt + 1, cnt + 2};
				type[cnt + 1] = 4, lb[cnt + 1] = l[i], rb[cnt + 1] = r[i];
			}
			TwoSAT::AddEdge(cnt + 2, cnt + 1), cnt += 2;
		} else rect.push_back(i);
	}
	
	for (int i : rect) { 
		bool lf = l[i] <= left && left <= r[i], rf = l[i] <= right && right <= r[i];
		bool df = d[i] <= down && down <= u[i], uf = d[i] <= up && up <= u[i];
		if (lf && rf) {
			type[cnt + 1] = 1, lb[cnt + 1] = d[i], rb[cnt + 1] = u[i];
			L[++ cntl] = Line{d[i], u[i], cnt + 1, cnt + 2}, cnt += 2;
			type[cnt + 1] = 2, lb[cnt + 1] = d[i], rb[cnt + 1] = u[i];
			R[++ cntr] = Line{d[i], u[i], cnt + 1, cnt + 2}, cnt += 2;
			TwoSAT::AddEdge(L[cntl].x2, R[cntr].x1);
			TwoSAT::AddEdge(R[cntr].x2, L[cntl].x1);
		}
		else if (lf && df) {
			type[cnt + 1] = 1, lb[cnt + 1] = d[i], rb[cnt + 1] = u[i];
			L[++ cntl] = Line{d[i], u[i], cnt + 1, cnt + 2}, cnt += 2;
			type[cnt + 1] = 3, lb[cnt + 1] = l[i], rb[cnt + 1] = r[i];
			D[++ cntd] = Line{l[i], r[i], cnt + 1, cnt + 2}, cnt += 2;
			TwoSAT::AddEdge(L[cntl].x2, D[cntd].x1);
			TwoSAT::AddEdge(D[cntd].x2, L[cntl].x1);
		}
		else if (lf && uf) {
			type[cnt + 1] = 1, lb[cnt + 1] = d[i], rb[cnt + 1] = u[i];
			L[++ cntl] = Line{d[i], u[i], cnt + 1, cnt + 2}, cnt += 2;
			type[cnt + 1] = 4, lb[cnt + 1] = l[i], rb[cnt + 1] = r[i];
			U[++ cntu] = Line{l[i], r[i], cnt + 1, cnt + 2}, cnt += 2;
			TwoSAT::AddEdge(L[cntl].x2, U[cntu].x1);
			TwoSAT::AddEdge(U[cntu].x2, L[cntl].x1);
		}
		else if (rf && df) {
			type[cnt + 1] = 2, lb[cnt + 1] = d[i], rb[cnt + 1] = u[i];
			R[++ cntr] = Line{d[i], u[i], cnt + 1, cnt + 2}, cnt += 2;
			type[cnt + 1] = 3, lb[cnt + 1] = l[i], rb[cnt + 1] = r[i];
			D[++ cntd] = Line{l[i], r[i], cnt + 1, cnt + 2}, cnt += 2;
			TwoSAT::AddEdge(R[cntr].x2, D[cntd].x1);
			TwoSAT::AddEdge(D[cntd].x2, R[cntr].x1);
		}
		else if (rf && uf) {
			type[cnt + 1] = 2, lb[cnt + 1] = d[i], rb[cnt + 1] = u[i];
			R[++ cntr] = Line{d[i], u[i], cnt + 1, cnt + 2}, cnt += 2;
			type[cnt + 1] = 4, lb[cnt + 1] = l[i], rb[cnt + 1] = r[i];
			U[++ cntu] = Line{l[i], r[i], cnt + 1, cnt + 2}, cnt += 2;
			TwoSAT::AddEdge(R[cntr].x2, U[cntu].x1);
			TwoSAT::AddEdge(U[cntu].x2, R[cntr].x1);
		}
		else if (df && uf) {
			type[cnt + 1] = 3, lb[cnt + 1] = l[i], rb[cnt + 1] = r[i];
			D[++ cntd] = Line{l[i], r[i], cnt + 1, cnt + 2}, cnt += 2;
			type[cnt + 1] = 4, lb[cnt + 1] = l[i], rb[cnt + 1] = r[i];
			U[++ cntu] = Line{l[i], r[i], cnt + 1, cnt + 2}, cnt += 2;
			TwoSAT::AddEdge(D[cntd].x2, U[cntu].x1);
			TwoSAT::AddEdge(U[cntu].x2, D[cntd].x1);
		}
	}
	
	TwoSAT::n = cnt;
	std::sort(L + 1, L + cntl + 1);
	std::sort(R + 1, R + cntr + 1);
	std::sort(D + 1, D + cntd + 1);
	std::sort(U + 1, U + cntu + 1);
	for (int i = 1; i <= cntl; ++ i) {
		int j = std::lower_bound(L + 1, L + i + 1, L[i].l) - L - 1;
		if (j) TwoSAT::AddEdge(L[i].x1, prel1[j]), TwoSAT::AddEdge(prel2[j], L[i].x2);
		prel1[i] = ++ cnt, prel2[i] = ++ cnt;
		TwoSAT::AddEdge(prel1[i], L[i].x2), TwoSAT::AddEdge(L[i].x1, prel2[i]);
		if (i > 1) TwoSAT::AddEdge(prel1[i], prel1[i - 1]), TwoSAT::AddEdge(prel2[i - 1], prel2[i]);
	}
	for (int i = 1; i <= cntr; ++ i) {
		int j = std::lower_bound(R + 1, R + i + 1, R[i].l) - R - 1;
		if (j) TwoSAT::AddEdge(R[i].x1, prer1[j]), TwoSAT::AddEdge(prer2[j], R[i].x2);
		prer1[i] = ++ cnt, prer2[i] = ++ cnt;
		TwoSAT::AddEdge(prer1[i], R[i].x2), TwoSAT::AddEdge(R[i].x1, prer2[i]);
		if (i > 1) TwoSAT::AddEdge(prer1[i], prer1[i - 1]), TwoSAT::AddEdge(prer2[i - 1], prer2[i]);
	}
	for (int i = 1; i <= cntd; ++ i) {
		int j = std::lower_bound(D + 1, D + i + 1, D[i].l) - D - 1;
		if (j) TwoSAT::AddEdge(D[i].x1, pred1[j]), TwoSAT::AddEdge(pred2[j], D[i].x2);
		pred1[i] = ++ cnt, pred2[i] = ++ cnt;
		TwoSAT::AddEdge(pred1[i], D[i].x2), TwoSAT::AddEdge(D[i].x1, pred2[i]);
		if (i > 1) TwoSAT::AddEdge(pred1[i], pred1[i - 1]), TwoSAT::AddEdge(pred2[i - 1], pred2[i]);
	}
	for (int i = 1; i <= cntu; ++ i) {
		int j = std::lower_bound(U + 1, U + i + 1, U[i].l) - U - 1;
		if (j) TwoSAT::AddEdge(U[i].x1, preu1[j]), TwoSAT::AddEdge(preu2[j], U[i].x2);
		preu1[i] = ++ cnt, preu2[i] = ++ cnt;
		TwoSAT::AddEdge(preu1[i], U[i].x2), TwoSAT::AddEdge(U[i].x1, preu2[i]);
		if (i > 1) TwoSAT::AddEdge(preu1[i], preu1[i - 1]), TwoSAT::AddEdge(preu2[i - 1], preu2[i]);
	}
	ans = TwoSAT::solve();
	int leftup = up, leftdown = down, rightup = up, rightdown = down;
	int upleft = left, upright = right, downleft = left, downright = right;
	for (int i : ans)
		if (type[i] == 1) leftup = min(leftup, rb[i]), leftdown = max(leftdown, lb[i]);
		else if (type[i] == 2) rightup = min(rightup, rb[i]), rightdown = max(rightdown, lb[i]);
		else if (type[i] == 3) downright = min(downright, rb[i]), downleft = max(downleft, lb[i]);
		else if (type[i] == 4) upright = min(upright, rb[i]), upleft = max(upleft, lb[i]);
	printf("%d %d\n%d %d\n%d %d\n%d %d", left, leftup, right, rightup, downleft, down, upleft, up);
	return 0;
}
posted @ 2022-03-24 12:15  zqs2020  阅读(68)  评论(0编辑  收藏  举报