题解:CF70D Professor's task

题意

实现以下两种操作:

  1. 往点集 S 中添加一个点 (x,y)

  2. 询问点 (x,y) 是否在点集 S 的凸包中。

分析

动态凸包板子。

建议先完成 P2521 [HAOI2011] 防线修建

上题维护的是上半个凸包,本题维护上下两个。

将凸包中的点按 x 排序,通过 (x,y) 前驱和后继连成的直线判断 (x,y) 是否位于凸包内。

若在凸包外,则加入这个点,并删除当前凸包中所有不符合凸包性质的点。


考虑使用平衡树。

既然只用查前驱和后继,用 set 就能解决。

为了便于计算,定义结构体 vec

struct vec
{
double x, y;
vec(double X=0, double Y=0): x(X), y(Y) {}
friend vec operator-(vec a, vec b) {return {a.x-b.x, a.y-b.y};}
friend double cross(vec a, vec b) {return a.x*b.y-a.y*b.x;}
bool operator<(vec b) const {return x<b.x;}
};

因为要维护上下两个凸包,再封装一个结构体存凸包。

struct convex_hull:set<vec>
{
auto pre(iterator it) {return --it;}
auto aft(iterator it) {return ++it;}
bool is_up; // 记录该凸包为上凸包还是下凸包
convex_hull(bool x): is_up(x) {}

通过前驱和后继检查该点是否在凸包内:

bool chk(vec A)
{
auto it=lower_bound(A);
if(it==end()) return 0;
if(it->x==A.x)
return is_up?it->y>=A.y:it->y<=A.y;
if(it==begin()) return 0;
vec B=*it, C=*--it;
double st=cross(B-A, B-C);
return is_up?st<=0:st>=0;
}

从凸包中移除一个点:

bool remove(set<vec>::iterator it)
{
if(it==begin()) return 0;
auto itl=pre(it);
auto itr=aft(it);
if(itr==end()) return 0;
vec a=*it-*itl, b=*itr-*it;
if(is_up?cross(a, b)<0:cross(a, b)>0) return 0;
return erase(it), 1;
}

向凸包中加入一个点:

void append(vec A)
{
if(chk(A)) return;
auto it=find(A);
if(it!=end()) erase(it);
it=insert(A).first;
while(it!=begin()&&remove(pre(it)));
while(aft(it)!=end()&&remove(aft(it)));
}

每个点最多进一次凸包点集,也最多出一次凸包点集,时间复杂度 O(nlogn)

AC 记录

本文作者:Jimmy-LEEE

本文链接:https://www.cnblogs.com/redacted-area/p/18379566

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Jimmy-LEEE  阅读(5)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起