CF70D Professor's task
两种操作:
- 1 往点集 S 中添加一个点 \((x, y)\);
- 2 询问 \((x,y)\) 是否在点集 S 的凸包中。
数据保证至少有一个 \(2\) 操作,保证刚开始会给出三个 \(1\) 操作, 且这三个操作中的点不共线。\(n \le 10^5\)
计算几何
凸包
平衡树
动态凸包板子题目,考虑正常的凸包维护方式:
- \(\text{x-y}\) 排序,优先 \(x\) 坐标小的,然后再是 \(y\) 坐标小的。
- 使用单调栈维护上凸壳、下凸壳(根据叉积判断)。
对于这个题目,我们可以使用平衡树维护 \(\text{x-y}\) 排序的结果,每次插入的时候判断是否已经在凸包里面,然后再模拟单调栈进行弹栈。
具体实现的时候可以拿两个 struct 分别维护上下凸壳,通过 template 传参来判断不同的条件减少代码量,但是要注意几个细节:
- 当 \(x\) 相同的时候,凸包上面只能留一个点。
- 注意 template 传参后叉积为 \(0\) 的判断。
代码:
#include
#define in read()
using namespace std;
using ll = long long;
const int N = 1e5 + 10;
const int inf = 1e9;
struct vect {
int x, y;
vect (int _x = 0, int _y = 0) : x(_x), y(_y) { }
bool friend operator < (vect a, vect b) { return a.x ^ b.x ? a.x < b.x : a.y < b.y; }
vect friend operator - (vect a, vect b) { return vect(a.x - b.x, a.y - b.y); }
ll friend operator ^ (vect a, vect b) { return 1ll * a.x * b.y - 1ll * a.y * b.x; }
};
template < int N, bool op > struct convex_hull { // op = 0 : 上凸壳, op = 1 : 下凸壳
set < vect > al;
bool valid(vect a, vect b, vect c) { // in the convex hull
ll res = (a - b) ^ (c - b);
return (res == 0) || ((res < 0) ^ op);
}
bool check(vect x) { // in the convex hull
auto it = al.lower_bound(vect(x.x, -inf)); if(it == al.end()) return false;
if((*it).x == x.x) return (*it).y == x.y || (((*it).y > x.y) ^ op);
if(it != al.begin()) return valid(*prev(it), x, *it); return false;
}
bool remove(set < vect > :: iterator it) {
if(it == al.begin() || next(it) == al.end()) return false;
if(valid(*prev(it), *it, *next(it))) return al.erase(it), true;
return false;
}
void insert(vect x) {
if(check(x)) return; auto it = al.lower_bound(vect(x.x, -inf));
if(it != al.end() && (*it).x == x.x) al.erase(it);
al.insert(x); it = al.find(x);
if(it != al.begin()) { it--; while(remove(it++)) it--; }
if(++it != al.end()) { while(remove(it--)) it++; }
}
};
convex_hull < N, 0 > up;
convex_hull < N, 1 > down;
int main() {
int Q = in;
while(Q--) {
int op = in, x = in, y = in; vect t(x, y);
if(op == 1) up.insert(t), down.insert(t);
else puts(up.check(t) && down.check(t) ? "YES" : "NO");
}
return 0;
}