题解 gym102900J 【Octasection】
一个最 naive 的暴力是把 \(l_{1, i}, l_{2, i}, l_{3, i}\) 分别拿出来离散化后枚举判断,时间复杂度为 \(O(n^4)\)。
考虑减小枚举量:可以发现在枚举第一维后,对于一个还不合法的项 \(i\),若第二维 \(\not\in [l_{2, i}, r_{2, i}]\),第三维就必须选 \([l_{3, i}, r_{3, i}]\) 中的值。于是枚举第一维后扫描线即可。时间复杂度为 \(O(n^2 \log n)\)。
但这样还是太慢了,注意到项的不合法位置为前后缀各一处,考虑将问题转化为:
- 我们对第二维的每个位置维护另一维可以选的位置,这里我们用若干区间的交来描述。
- 第二维区间加入区间。
- 第二维区间删除区间。
- 询问是否存在一个第二维的位置,使得其区间交非空。
考虑线段树分治将第二种操作变为撤销第二维区间加入区间的操作。
首先考虑操作一、三。一个自然的想法是对第二维的每个位置维护 \([L_i, R_i]\) 表示区间交的左右端点,则操作一相当于区间对 \(L_i\) chkmax、对 \(R_i\) chkmin,操作三相当于询问 \(\displaystyle\max_i (R_i - L_i) \geq 0\) 是否成立。
注意到这里的操作区间一定是一个前缀或一个后缀,于是 \(L_i\) 单谷、\(R_i\) 单峰,我们每次操作的有效部分要么是全体、要么是前后缀,于是线段树上二分出修改区间,问题转化为区间赋值,这是容易维护的。
- 事实上我们实现时并不需要真的在线段树上二分:直接暴力递归,遇到 \(L_i\) 最小值 \(\geq k\)、\(R_i\) 最大值 \(\leq k\) 的情况就直接返回。
用栈维护所有有修改的线段树节点,撤销时弹栈即可。时间复杂度为 \(O(n \log^2 n)\)。
代码:
#include <iostream>
#include <algorithm>
#include <stack>
#include <vector>
#include <cstdio>
using namespace std;
typedef struct Rectangle_tag {
int x1;
int x2;
int y1;
int y2;
Rectangle_tag(){}
Rectangle_tag(int x1_, int x2_, int y1_, int y2_){
x1 = x1_;
x2 = x2_;
y1 = y1_;
y2 = y2_;
}
} Rectangle;
namespace SolveY {
typedef struct {
int l;
int r;
int chkmaxl;
int chkminr;
int minl;
int maxl;
int minr;
int maxr;
pair<int, int> pr1;
pair<int, int> pr2;
pair<int, int> pr3;
} SegmentTreeNode;
typedef struct StackNode_tag {
int tm;
int pos;
SegmentTreeNode info;
StackNode_tag(int tm_, int pos_, SegmentTreeNode info_){
tm = tm_;
pos = pos_;
info = info_;
}
} StackNode;
int top = 0, tm;
SegmentTreeNode tree[400007];
stack<StackNode> s;
inline void update(int x){
int ls = x * 2, rs = x * 2 + 1;
tree[x].minl = min(tree[ls].minl, tree[rs].minl);
tree[x].maxl = max(tree[ls].maxl, tree[rs].maxl);
tree[x].minr = min(tree[ls].minr, tree[rs].minr);
tree[x].maxr = max(tree[ls].maxr, tree[rs].maxr);
tree[x].pr1 = min(tree[ls].pr1, tree[rs].pr1);
tree[x].pr2 = max(tree[ls].pr2, tree[rs].pr2);
tree[x].pr3 = max(tree[ls].pr3, tree[rs].pr3);
}
void build(int x, int l, int r, int n){
tree[x].l = l;
tree[x].r = r;
tree[x].chkmaxl = 0;
tree[x].chkminr = 0x7fffffff;
if (l == r){
tree[x].minl = tree[x].maxl = 0;
tree[x].minr = tree[x].maxr = n;
tree[x].pr1 = make_pair(0, l);
tree[x].pr2 = tree[x].pr3 = make_pair(n, l);
return;
}
int mid = (l + r) >> 1;
build(x * 2, l, mid, n);
build(x * 2 + 1, mid + 1, r, n);
update(x);
}
inline void push(int x){
s.push(StackNode(tm, x, tree[x]));
}
inline bool check(int x){
return tree[x].chkmaxl != 0 || tree[x].chkminr != 0x7fffffff;
}
inline void push_chkmaxl(int x, int k){
tree[x].chkmaxl = tree[x].minl = tree[x].maxl = tree[x].pr1.first = k;
tree[x].pr3 = make_pair(tree[x].pr2.first - k, tree[x].pr2.second);
}
inline void push_chkminr(int x, int k){
tree[x].chkminr = tree[x].minr = tree[x].maxr = tree[x].pr2.first = k;
tree[x].pr3 = make_pair(k - tree[x].pr1.first, tree[x].pr1.second);
}
inline void pushdown(int x){
int ls = x * 2, rs = x * 2 + 1;
if (tree[x].chkmaxl != 0){
push_chkmaxl(ls, tree[x].chkmaxl);
push_chkmaxl(rs, tree[x].chkmaxl);
tree[x].chkmaxl = 0;
}
if (tree[x].chkminr != 0x7fffffff){
push_chkminr(ls, tree[x].chkminr);
push_chkminr(rs, tree[x].chkminr);
tree[x].chkminr = 0x7fffffff;
}
}
void chkmaxl(int x, int l, int r, int k){
if (tree[x].minl >= k) return;
push(x);
if (l <= tree[x].l && tree[x].r <= r && tree[x].maxl < k){
push_chkmaxl(x, k);
return;
}
int mid = (tree[x].l + tree[x].r) >> 1;
if (check(x)){
push(x * 2);
push(x * 2 + 1);
pushdown(x);
}
if (l <= mid) chkmaxl(x * 2, l, r, k);
if (r > mid) chkmaxl(x * 2 + 1, l, r, k);
update(x);
}
void chkminr(int x, int l, int r, int k){
if (tree[x].maxr <= k) return;
push(x);
if (l <= tree[x].l && tree[x].r <= r && tree[x].minr > k){
push_chkminr(x, k);
return;
}
int mid = (tree[x].l + tree[x].r) >> 1;
if (check(x)){
push(x * 2);
push(x * 2 + 1);
pushdown(x);
}
if (l <= mid) chkminr(x * 2, l, r, k);
if (r > mid) chkminr(x * 2 + 1, l, r, k);
update(x);
}
int getl(int x, int pos){
if (tree[x].l == tree[x].r) return tree[x].minl;
pushdown(x);
if (pos <= ((tree[x].l + tree[x].r) >> 1)) return getl(x * 2, pos);
return getl(x * 2 + 1, pos);
}
inline bool rollback(){
if (s.empty()) return false;
StackNode cur = s.top();
if (cur.tm != tm) return false;
s.pop();
tree[cur.pos] = cur.info;
return true;
}
}
int lst1[100007], lst2[100007], lst3[100007];
namespace SolveX {
typedef struct {
int l;
int r;
vector<Rectangle> v;
} Node;
Node tree[400007];
void build(int x, int l, int r){
tree[x].l = l;
tree[x].r = r;
if (l == r) return;
int mid = (l + r) >> 1;
build(x * 2, l, mid);
build(x * 2 + 1, mid + 1, r);
}
void insert(int x, int l, int r, Rectangle rec){
if (l <= tree[x].l && tree[x].r <= r){
tree[x].v.push_back(rec);
return;
}
int mid = (tree[x].l + tree[x].r) >> 1;
if (l <= mid) insert(x * 2, l, r, rec);
if (r > mid) insert(x * 2 + 1, l, r, rec);
}
void dfs(int x, int n){
SolveY::tm++;
for (register Rectangle i : tree[x].v){
if (i.x1 > 1){
SolveY::chkmaxl(1, 1, i.x1 - 1, i.y1);
SolveY::chkminr(1, 1, i.x1 - 1, i.y2);
}
if (i.x2 < n){
SolveY::chkmaxl(1, i.x2 + 1, n, i.y1);
SolveY::chkminr(1, i.x2 + 1, n, i.y2);
}
}
if (tree[x].l == tree[x].r){
if (SolveY::tree[1].pr3.first >= 0){
cout << "YES" << endl;
cout << lst1[tree[x].l] << " " << lst2[SolveY::tree[1].pr3.second] << " " << lst3[SolveY::getl(1, SolveY::tree[1].pr3.second)];
exit(0);
}
} else {
dfs(x * 2, n);
dfs(x * 2 + 1, n);
}
while (SolveY::rollback()) ;
SolveY::tm--;
}
}
int l1[100007], r1[100007], l2[100007], r2[100007], l3[100007], r3[100007];
int main(){
int n, cnt1 = 0, cnt2 = 0, cnt3 = 0;
scanf("%d", &n);
for (register int i = 1; i <= n; i++){
scanf("%d %d %d %d %d %d", &l1[i], &r1[i], &l2[i], &r2[i], &l3[i], &r3[i]);
lst1[++cnt1] = l1[i];
lst2[++cnt2] = l2[i];
lst3[++cnt3] = l3[i];
}
sort(lst1 + 1, lst1 + cnt1 + 1);
cnt1 = unique(lst1 + 1, lst1 + cnt1 + 1) - lst1 - 1;
sort(lst2 + 1, lst2 + cnt2 + 1);
cnt2 = unique(lst2 + 1, lst2 + cnt2 + 1) - lst2 - 1;
sort(lst3 + 1, lst3 + cnt3 + 1);
cnt3 = unique(lst3 + 1, lst3 + cnt3 + 1) - lst3 - 1;
SolveX::build(1, 1, cnt1);
for (register int i = 1; i <= n; i++){
Rectangle rect;
l1[i] = lower_bound(lst1 + 1, lst1 + cnt1 + 1, l1[i]) - lst1;
r1[i] = upper_bound(lst1 + 1, lst1 + cnt1 + 1, r1[i]) - lst1 - 1;
l2[i] = lower_bound(lst2 + 1, lst2 + cnt2 + 1, l2[i]) - lst2;
r2[i] = upper_bound(lst2 + 1, lst2 + cnt2 + 1, r2[i]) - lst2 - 1;
l3[i] = lower_bound(lst3 + 1, lst3 + cnt3 + 1, l3[i]) - lst3;
r3[i] = upper_bound(lst3 + 1, lst3 + cnt3 + 1, r3[i]) - lst3 - 1;
rect = Rectangle(l2[i], r2[i], l3[i], r3[i]);
if (l1[i] > 1) SolveX::insert(1, 1, l1[i] - 1, rect);
if (r1[i] < cnt1) SolveX::insert(1, r1[i] + 1, cnt1, rect);
}
SolveY::build(1, 1, cnt2, cnt3);
SolveX::dfs(1, cnt2);
cout << "NO";
return 0;
}

浙公网安备 33010602011771号