题意
给定若干个点,实现下列操作:
- 删除一个点。
- 查询上凸包的周长。
分析
建议先完成【模板】二维凸包。
对于这个删除操作,我们没有好的方法去在线维护它。
考虑离线询问。
这样删除操作就变成了插入操作。
插入一个新点有如下两种情况:
- 新点在凸包内。
- 新点在凸包外。
我们回忆一下 Andrew 算法的过程:先将点按 \(x\) 排序,然后单调栈维护凸包。
这给了我们启示,如果一个点 \((x_i,y_i)\) 能加入凸包中,那么原凸包中和直线 \(x=x_i\) 相交的边一定被删除。
将原点集按 \(x\) 排序,这条边连接的两个点显然就是 \((x_i,y_i)\) 的前驱和后继。
考虑用平衡树维护,判断点 \((x_i,y_i)\) 与该边的位置关系即可。
在凸包内那么就不用更新凸包。
考虑该点在凸包外的情况。
我们先将该点加入凸包并更新答案。
我们从该点出发,向前后分别扫一遍,将不符合凸包性质的点删除并更新答案。
如果一个点符合凸包性质,那么显然它前或后的点也满足凸包性质,就结束循环。
每个点最多进一次凸包点集,也最多出一次凸包点集,时间复杂度 \(O(m\log m)\)。
Code
#include<bits/stdc++.h>
using namespace std;
#define maxn 100005
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 int cross (vec a, vec b) {return a.x*b.y-a.y*b.x; }
auto operator<=> (vec b) const {return x==b.x?y<=>b.y:x<=>b.x;}
double length () {return sqrt(x*x+y*y); }
};
set<vec> s;
double ans;
bool chk(vec A)
{
auto it=s.lower_bound(A);
vec B=*it, C=*--it;
return cross(B-A, B-C)<=0;
}
auto pre(set<vec>::iterator it) {return --it;}
auto aft(set<vec>::iterator it) {return ++it;}
bool erase(set<vec>::iterator it)
{
if(it==s.begin()) return 0;
auto itl=pre(it);
auto itr=aft(it);
if(itr==s.end()) return 0;
vec a=*it-*itl, b=*itr-*it;
if(cross(a, b)<0) return 0;
ans+=(*itr-*itl).length()-a.length()-b.length();
s.erase(it);
return 1;
}
void insert(vec A)
{
if(chk(A)) return;
auto it=s.insert(A).first;
auto pr=pre(it);
auto af=aft(it);
ans+=(*it-*pr).length()+(*it-*af).length()-(*af-*pr).length();
while(erase(pre(it)));
while(erase(aft(it)));
}
vector<pair<int, int>> vc;
vec dts[maxn];
bool del[maxn];
vector<double> Ans;
int main()
{
int n, m, x0, y0;
cin>>n>>x0>>y0>>m;
for(int i=1;i<=m;i++)
cin>>dts[i].x>>dts[i].y;
int q;
cin>>q;
for(int i=1, op, x=0;i<=q;i++)
{
cin>>op;
if(op==1) cin>>x, del[x]=1;
vc.emplace_back(op, x);
}
s.emplace(0, 0);
s.emplace(n, 0);
s.emplace(x0, y0);
ans=vec(x0, y0).length()+vec(x0-n, y0).length();
for(int i=1;i<=m;i++) if(!del[i]) insert(dts[i]);
for(auto [op, x]:views::reverse(vc))
{
if(op==1) insert(dts[x]);
else Ans.emplace_back(ans);
}
for(auto i:views::reverse(Ans))
printf("%.2lf\n", i);
}
标签:return,HAOI2011,题解,凸包,int,vec,auto,y0,P2521
From: https://www.cnblogs.com/redacted-area/p/18379565