题目链接

点击打开链接

题目解法

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

考虑在原问题中,一个确定的方案 ( x 1 , y 1 ) , ( x 2 , y 2 ) , … (x_1,y_1),(x_2,y_2),\dots (x1​,y1​),(x2​,y2​),… 。
若存在某个点 ( x i , y i ) (x_i,y_i) (xi​,yi​) ,使得其余所有点均在其左下 / 左上 / 右下 / 右上方,那么将其改写为
( 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{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\{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​})
中的一者加入答案,并删去被覆盖到的矩形。

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

对于 K = 4 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) (x0​,min{Ui​}),(min{Ri​},y0​),(x1​,max{Di​}),(max{Li​},y1​)
且 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\} min{Ri​}≤x0​,x1​≤max{Li​},min{Ui​}≤y0​,y1​≤max{Di​}

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

对坐标离散化,考虑枚举 x 0 x_0 x0​ 。
由于枚举完 x 0 x_0 x0​ 后,问题变为了 K = 3 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\}) (min{Ri​},min{Ui​}),(min{Ri​},max{Di​}),(max{Li​},min{Ui​}),(max{Li​},max{Di​})
因此, y 0 , y 1 y_0,y_1 y0​,y1​ 中至少存在一者是取到此时的 min ⁡ { U i } \min\{U_i\} min{Ui​} 的。
不妨令 y 0 y_0 y0​ 取到 min ⁡ { U i } \min\{U_i\} min{Ui​} ,即不违反 x 0 , y 0 x_0,y_0 x0​,y0​ 间限制的最大值。

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

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

时间复杂度 O ( 4 K × N + N L o g N ) O(4^K\times N+NLogN) O(4K×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;
}

【LOJ3272】「JOISC 2020 Day1」汉堡肉相关推荐

  1. 「JOISC 2020 Day4」治疗计划(线段树+dijkstra最短路)

    「JOISC 2020 Day4」治疗计划 description solution 设dpi:1−Ridp_i:1-R_idpi​:1−Ri​ 都能被救治成功的最小花费 两个治疗方案[Li,Ri], ...

  2. 【LOJ】#3030. 「JOISC 2019 Day1」考试

    LOJ#3030. 「JOISC 2019 Day1」考试 看起来求一个奇怪图形(两条和坐标轴平行的线被切掉了一个角)内包括的点个数 too naive! 首先熟练的转化求不被这个图形包含的个数 -- ...

  3. 「JOISC 2014 Day1」巴士走读

    「JOISC 2014 Day1」巴士走读 题解部分: (如果不怎么喜欢看推导的人可以直接看下面的关键部分,在段尾会有标注(或者看完定义直接看代码)) 本题让我们求到达点n需要最晚何时到达点1,我们可 ...

  4. 「JOISC 2014 Day3」稻草人

    「JOISC 2014 Day3」稻草人 问题简述 解析 代码 问题简述 链接 https://loj.ac/problem/2880   给定 n n n个稻草人(横纵坐标是不大于 1 0 9 10 ...

  5. bzoj4244 loj2878. 「JOISC 2014 Day2」邮戳拉力赛 括号序列+背包

    题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4244 https://loj.ac/problem/2878 题解 挺妙的一道题. 一开始一直 ...

  6. LOJ #2878. 「JOISC 2014 Day2」邮戳拉力赛 动态规划+括号序列

    神题呀,我们观察到行走的方式一定是一条链+若干条环. 然后环可以看成是一对括号,所以来一个基于括号序的 DP. code: #include <bits/stdc++.h> #define ...

  7. [LOJ#2878]. 「JOISC 2014 Day2」邮戳拉力赛[括号序列dp]

    题意 题目链接 分析 如果走到了下行车站就一定会在前面的某个车站走回上行车站,可以看成是一对括号. 我们要求的就是 类似 代价最小的括号序列匹配问题,定义 f(i,j) 表示到 i 有 j 个左括号没 ...

  8. 「JOISC 2014 Day4」挂饰(背包DP)题解

    题目翻译 JOI 君有 n n n 个装在手机上的挂饰,编号为 1 - n 1 \ldots n 1-n. JOI 君可以将其中一些挂饰装在手机上. JOI 君的挂饰有一些与众不同--其中的一些挂饰附 ...

  9. 【题解】LOJ3254:「JOI 2020 Final」集邮比赛 3

    原题传送门 发现 n < = 200 n<=200 n<=200,那么我们就可以想一个 O ( n 3 ) O(n^3) O(n3)的dp 发现每次肯定是向左/右推进 可以令 d p ...

最新文章

  1. yum安装Imagick及扩展
  2. 百练OJ:2713:肿瘤面积
  3. P1829 [国家集训队]Crash的数字表格(推了好久的mobius反演)
  4. aix 的c库为什么都是静态库_Linux静态库生成指南
  5. 再回首Java第二天
  6. 字节跳动2021春招启动 提供超7000个岗位
  7. VS2008 清理注册表
  8. 中文知识图谱研讨会的学习总结 (上) 图谱引入、百度知心、搜狗知立方
  9. 调试经验——Windows10中iTunes不能识别iPad的解决方法
  10. 使用短信接口进行通知
  11. 【Java 后端接收前端的富文本数据,其中标签样式数据丢失解决】
  12. SDL2音视频渲染入门
  13. vue 项目获取QQ音乐歌单数据
  14. linux 日历,计算器,nano编辑器,开关机、重启,
  15. linux进程暂停与恢复
  16. 如何强力卸载流氓软件
  17. 移动APP设计国外资源总汇
  18. 《Python自然语言处理-雅兰·萨纳卡(Jalaj Thanaki)》学习笔记:06 高级特征工程和NLP算法
  19. 配置小程序开发者工具及其使用(下)
  20. exoplayer2同时播放多个音频文件

热门文章

  1. python进阶书目串烧(八)
  2. 关于run time error的处理
  3. python turtle 椭圆_Python易学就会(五)turtle绘制椭圆与递归-Go语言中文社区
  4. 案例分析 | 宜家以双钻设计模型探索线上零售新业务
  5. 违章了多久能查到,在哪里可以查违章的?
  6. Java多线程基础三 sleep的中断
  7. illustrator插件-画板功能开发-多图层转多画板-js脚本开发-ai插件
  8. react 地区筛选_React 与 Redux 实践 —— 城市筛选面板
  9. redis设置用户名和密码linux,redis的密码设置(windows与linux相同)
  10. 提交表单POST请求