向天杰的动态凸包

还记得普通凸包里的那个while循环吗?我们现在只要把这个while循环给改一下,让每次新加入的点 按照极角序 往前 / 后遍历,我们就可以在平均复杂度 O(?) 的情况下求出来新的凸包。      我也不知道具体多少,但大多数情况下很快,至少不用遍历所有点

如果题目有额外要求,我们可以直接在插入函数里面进行维护,如本模板就是维护了新凸包的周长。

本模板其实并非插入点,而是删除点,大家可以自己好好体会。

关键是用set存点,然后找到前驱和后继结点,大家重点看Pre Nxt两个函数就好,其他的无非就是静态凸包的变形和set的应用

模板根据此题缩写 : 题目链接

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define Pair pair<int,int>
#define clean(a,b) memset(a,b,sizeof(a))// 水印
const int MAXN=1e5+10;
const double EPS=1.0e-8;
const double PI=acos(-1.0);
struct Point {
	double x,y,vis,l;
	Point(double _x=0,double _y=0,double _vis=0,double _l=0) {
		x=_x;
		y=_y;
		vis=_vis;
		l=_l;
	}
	friend Point operator + (const Point &a,const Point &b) {
		return Point(a.x+b.x,a.y+b.y);
	}
	friend Point operator - (const Point &a,const Point &b) {
		return Point(a.x-b.x,a.y-b.y);
	}
	friend double operator ^ (Point a,Point b) { //向量叉乘
		return a.x*b.y-a.y*b.x;
	}
	friend bool operator == (const Point &a,const Point &b) {
		if(fabs(a.x-b.x)<EPS&&fabs(a.y-b.y)<EPS) return true;
		return false;
	}
};
Point Basic,Dots[MAXN];
set<Point> Set;
Pair q[MAXN];
int vis[MAXN];
double ans[MAXN],ansl;
double dis(Point a,Point b) {
	return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
bool operator < (Point a,Point b) {
	a=a-Basic;
	b=b-Basic;
	double Ang1=atan2(a.y,a.x),Ang2=atan2(b.y,b.x);
	double Len1=dis(a,Point(0,0)),Len2=dis(b,Point(0,0));
	if(fabs(Ang1-Ang2)<EPS) return Len1<Len2;
	return Ang1<Ang2;
}
set<Point>::iterator Pre(set<Point>::iterator it) {//set中it的前驱结点 
	if(it==Set.begin()) it=Set.end();
	return --it;
}
set<Point>::iterator Nxt(set<Point>::iterator it) {//set中it的后继结点
	++it;
	return it==Set.end()?Set.begin():it;
}
int Query(Point p) {//查询点是否在凸包内 
	set<Point>::iterator it=Set.lower_bound(p);
	if(it==Set.end()) it=Set.begin();
	return ((p- *(Pre(it)))^(*(it)-*(Pre(it))))<EPS;//ans<=0在里面||在线上
}
void Insert(Point p) {
	if(Query(p)) return ;//点在凸包内部,则无需进行更新,直接跳出即可 
	Set.insert(p);
	set<Point>::iterator it;
	it=Set.find(p); 
	ansl-=dis(*(Nxt(it)),*(Pre(it)));//维护本题的答案,即凸包周长,下面的ansl的处理,与此同理 
	ansl+=dis(*(it),*(Pre(it)));
	ansl+=dis(*(it),*(Nxt(it)));
	it=Pre(Set.find(p));
	while(Set.size()>3&&((p- *(it))^(*(it)- *(Pre(it))))>-EPS) {//按照极角序往前遍历,极端情况下遍历之前几乎所有点 
		ansl-=dis(*(it),*(Nxt(it)));
		ansl-=dis(*(it),*(Pre(it)));
		ansl+=dis(*(Nxt(it)),*(Pre(it)));
		Set.erase(it);
		it=Pre(Set.find(p));
	}
	it=Nxt(Set.find(p));
	while(Set.size()>3&&((p- *(Nxt(it)))^(*(it)- *(Nxt(it))))<EPS) {//按照极角序往前遍历,极端情况下遍历之前几乎所有点 
		ansl-=dis(*(it),*(Nxt(it)));
		ansl-=dis(*(it),*(Pre(it)));
		ansl+=dis(*(Nxt(it)),*(Pre(it)));
		Set.erase(it);
		it=Nxt(Set.find(p));
	}
}
int main() {
	double n,x,y;
	scanf("%lf%lf%lf",&n,&x,&y);
	Basic=Point(0,0);//极角排序基准点 
	ansl=0;
	int m;
	scanf("%d",&m);
	for(int i=1; i<=m; ++i) {
		scanf("%lf%lf",&Dots[i].x,&Dots[i].y);
	}
	int Q,oper,Index;
	scanf("%d",&Q);
	clean(vis,0);
	for(int i=1; i<=Q; ++i) {
		scanf("%d",&oper);
		if(oper==2) {
			q[i]=make_pair(2,0);
		} else {
			scanf("%d",&Index);
			q[i]=make_pair(1,Index);
			vis[Index]=1;
		}
	}
	Set.insert(Point(0,0)),Set.insert(Point(n,0)),Set.insert(Point(x,y));
	ansl=dis(Point(x,y),Point(0,0))+dis(Point(x,y),Point(n,0));
	for(int i=1; i<=m; ++i) {
		if(vis[i]==0) {
			Insert(Dots[i]);
		}
	}
	int k=0;
	for(int i=Q; i>=1; --i) {
		if(q[i].first==2) {
			ans[k++]=ansl;
		} else {
			Insert(Dots[q[i].second]);
		}
	}
	for(int i=k-1; i>=0; --i) {
		printf("%.2lf\n",ans[i]);
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值