题解:CF70D Professor's task
题意
实现以下两种操作:
往点集
中添加一个点 。 询问点
是否在点集 的凸包中。
分析
动态凸包板子。
建议先完成 P2521 [HAOI2011] 防线修建。
上题维护的是上半个凸包,本题维护上下两个。
将凸包中的点按
若在凸包外,则加入这个点,并删除当前凸包中所有不符合凸包性质的点。
考虑使用平衡树。
既然只用查前驱和后继,用 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))); }
每个点最多进一次凸包点集,也最多出一次凸包点集,时间复杂度
本文作者:Jimmy-LEEE
本文链接:https://www.cnblogs.com/redacted-area/p/18379566
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
分类:
题解
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步