【LOJ3272】「JOISC 2020 Day1」汉堡肉
题目链接
点击打开链接
题目解法
考虑问题在一维上的形式,显然,我们会希望所选的最靠左侧的点尽量靠右。
因此,选择 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」汉堡肉相关推荐
- 「JOISC 2020 Day4」治疗计划(线段树+dijkstra最短路)
「JOISC 2020 Day4」治疗计划 description solution 设dpi:1−Ridp_i:1-R_idpi:1−Ri 都能被救治成功的最小花费 两个治疗方案[Li,Ri], ...
- 【LOJ】#3030. 「JOISC 2019 Day1」考试
LOJ#3030. 「JOISC 2019 Day1」考试 看起来求一个奇怪图形(两条和坐标轴平行的线被切掉了一个角)内包括的点个数 too naive! 首先熟练的转化求不被这个图形包含的个数 -- ...
- 「JOISC 2014 Day1」巴士走读
「JOISC 2014 Day1」巴士走读 题解部分: (如果不怎么喜欢看推导的人可以直接看下面的关键部分,在段尾会有标注(或者看完定义直接看代码)) 本题让我们求到达点n需要最晚何时到达点1,我们可 ...
- 「JOISC 2014 Day3」稻草人
「JOISC 2014 Day3」稻草人 问题简述 解析 代码 问题简述 链接 https://loj.ac/problem/2880 给定 n n n个稻草人(横纵坐标是不大于 1 0 9 10 ...
- bzoj4244 loj2878. 「JOISC 2014 Day2」邮戳拉力赛 括号序列+背包
题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4244 https://loj.ac/problem/2878 题解 挺妙的一道题. 一开始一直 ...
- LOJ #2878. 「JOISC 2014 Day2」邮戳拉力赛 动态规划+括号序列
神题呀,我们观察到行走的方式一定是一条链+若干条环. 然后环可以看成是一对括号,所以来一个基于括号序的 DP. code: #include <bits/stdc++.h> #define ...
- [LOJ#2878]. 「JOISC 2014 Day2」邮戳拉力赛[括号序列dp]
题意 题目链接 分析 如果走到了下行车站就一定会在前面的某个车站走回上行车站,可以看成是一对括号. 我们要求的就是 类似 代价最小的括号序列匹配问题,定义 f(i,j) 表示到 i 有 j 个左括号没 ...
- 「JOISC 2014 Day4」挂饰(背包DP)题解
题目翻译 JOI 君有 n n n 个装在手机上的挂饰,编号为 1 - n 1 \ldots n 1-n. JOI 君可以将其中一些挂饰装在手机上. JOI 君的挂饰有一些与众不同--其中的一些挂饰附 ...
- 【题解】LOJ3254:「JOI 2020 Final」集邮比赛 3
原题传送门 发现 n < = 200 n<=200 n<=200,那么我们就可以想一个 O ( n 3 ) O(n^3) O(n3)的dp 发现每次肯定是向左/右推进 可以令 d p ...
最新文章
- yum安装Imagick及扩展
- 百练OJ:2713:肿瘤面积
- P1829 [国家集训队]Crash的数字表格(推了好久的mobius反演)
- aix 的c库为什么都是静态库_Linux静态库生成指南
- 再回首Java第二天
- 字节跳动2021春招启动 提供超7000个岗位
- VS2008 清理注册表
- 中文知识图谱研讨会的学习总结 (上) 图谱引入、百度知心、搜狗知立方
- 调试经验——Windows10中iTunes不能识别iPad的解决方法
- 使用短信接口进行通知
- 【Java 后端接收前端的富文本数据,其中标签样式数据丢失解决】
- SDL2音视频渲染入门
- vue 项目获取QQ音乐歌单数据
- linux 日历,计算器,nano编辑器,开关机、重启,
- linux进程暂停与恢复
- 如何强力卸载流氓软件
- 移动APP设计国外资源总汇
- 《Python自然语言处理-雅兰·萨纳卡(Jalaj Thanaki)》学习笔记:06 高级特征工程和NLP算法
- 配置小程序开发者工具及其使用(下)
- exoplayer2同时播放多个音频文件
热门文章
- python进阶书目串烧(八)
- 关于run time error的处理
- python turtle 椭圆_Python易学就会(五)turtle绘制椭圆与递归-Go语言中文社区
- 案例分析 | 宜家以双钻设计模型探索线上零售新业务
- 违章了多久能查到,在哪里可以查违章的?
- Java多线程基础三 sleep的中断
- illustrator插件-画板功能开发-多图层转多画板-js脚本开发-ai插件
- react 地区筛选_React 与 Redux 实践 —— 城市筛选面板
- redis设置用户名和密码linux,redis的密码设置(windows与linux相同)
- 提交表单POST请求