前景提要:

继承上一张学习的凸包问题,下面我么来总结一下动态凸包的维护问题。

一些点已经构成了一个凸包之后,新加入||删除一些新的点的时候,会对原有的凸包产生一些影响,如果每次都重新把所有点都重新计算一遍凸包的话,那将非常耗费时间,以至于必定会WA,因此,我们就学习动态凸包的维护方法。

目录

前景提要:

观察总结凸包的维护

平衡树的维护

CF-70D-D. Professor's task(动态凸包板子)

洛谷-P2521-[HAOI2011]防线修建(离线查找+平衡树维护动态凸包)


观察总结凸包的维护

经过观察我们发现,每次新加入点的时候,只用对凸包的局部进行调整就行了,直接调整全部的凸包的话会花费很多不必要的时间和精力。因此有没有什么办法能够只调整凸包的部分呢?那就是找到需要调整的部分,然后以那个部分为中心开始调整,直到再次符合要求的时候即可。怎么才能快速找到要调整的部分呢?查找!平衡树!于是我们用平衡树来维护凸包,查找的时间就降低到了logn。!

首先得到一个最开始的凸包,我们用平衡树来维护这个凸包上的点,以极角序来作为第一关键字,长度作为第二关键字。来建立一颗平衡树。每次插入新点的时候,只用找到平衡树中距离这个新点最近的前面和后面的两个点,然后以这两个点为中心进行调整凸包。平衡树的查找时间复杂度为logn,这样只用进行局部调整就可以继续维持整个凸包的合理性了。

平衡树的维护

如何用平衡树来实现查找呢?如何建立平衡树呢?参考一下之前我的博客,伸展树维护平衡树:https://blog.csdn.net/qq_40482358/article/details/83305383

好了,现在我们已经回如何建立平衡树了。

但是平衡树的板子好长啊,光一个简单的板子就200多行了,就算格式紧凑一点也不好,自己写还容易出BUG。。不好不好。

当然我们可以不用自己写(那为什么放博客链接!!,当然是骗访问量啦!)

说笑说笑,其实学会平衡树之后对理解其他的数据结构也有很大的好处的,好处是巨大的。具体什么好处,太多了,就不多说了(有利于装逼)

C++《STL》内部的set集合就算是平衡树来维护的,因此我们可以借用一下set容器来实现我们的平衡树,而且平衡树的质量也有保证,不会出现一些奇奇怪怪的BUG...

至于如何用set维护我们的动态凸包,就是把点都加入set集合中,然后每次添加||删除的时候就是访问到set中距离新点最近的那几个点,(这几个点在凸包上),然后开始维护即可。

举个例子:如图所示,一开始存在一个凸包ABC(红色部分),然后新加入一个2号点,找到2号点附近的B和C点,然后删掉C点,加入2点;

再次新加一个1号点,找到A和2,发现无法删除,加入1号点即可。于是出现凸包AB21。

这里用两种方法进行排序,

1选择一个中心点,所有的点都在这个点的周围,以atan2的角度为第一关键字进行排序。但是因为涉及到了除法,因此会有精度损失,写一写水题还是可以的。

2.以凸包上的一个点为中间点进行极角排序,然后叉积进行第一关键字,这样就不会有精度损失。(但是我现在还不会QAQ)

直接上代码吧,首先是CF70D的一道板子题,大家也可以用来测试自己的板子:

CF-70D-D. Professor's task(动态凸包板子)

题目链接:http://codeforces.com/contest/70/problem/D

题目大意:给出n组情况,1添加一个点,2查看一个点是否在凸包内(包括边界)。对于每次查看,输出YES||NO。

思路:下面就是我的平衡树板子了。

ACCode:

#include<stdio.h>
#include<math.h>#include<set>
#include<iostream>#define ll long long
#define Pair pair<int,int>
using namespace std;
const int INF32=0x3f3f3f3f;
const double EPS=1.0e-8;
const double PI=acos(-1.0);struct Point{double x,y;Point(double _x=0,double _y=0){x=_x;y=_y;}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 ^ (const Point &a,const Point &b){return a.x*b.y-a.y*b.x;}friend bool operator == (const Point &a,const Point &b){return fabs(a.x-b.x)<EPS&&fabs(a.y-b.y)<EPS;}
};
struct V{Point start,end;V(Point _start=Point(0,0),Point _end=Point(0,0)){start=_start;end=_end;}
};
Point Basic;
set<Point> Set;double Distance(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=Distance(a,Point(0.0,0.0)),Len2=Distance(b,Point(0.0,0.0));if(fabs(Ang1-Ang2)<EPS) return Len1<Len2;return Ang1<Ang2;
}
set<Point>::iterator Pre(set<Point>::iterator it){if(it==Set.begin()) it=Set.end();return --it;
}
set<Point>::iterator Nxt(set<Point>::iterator 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;
}
void Insert(Point p){if(Query(p)) return ;Set.insert(p);set<Point>::iterator it=Nxt(Set.find(p));while(Set.size()>3&&((p- *(Nxt(it)))^(*(it)- *(Nxt(it))))<EPS){Set.erase(it);it=Nxt(Set.find(p));}it=Pre(Set.find(p));while(Set.size()>3&&((p- *(it))^(*(it)- *(Pre(it))))>-EPS){Set.erase(it);it=Pre(Set.find(p));}
}
int main(){int q;scanf("%d",&q);Basic=Point(0,0);int oper;Point a,str[5];for(int i=1;i<=3;++i){scanf("%d%lf%lf",&oper,&str[i].x,&str[i].y);Basic=Basic+str[i];//Set.insert(str[i]);}Basic.x/=3.0;Basic.y/=3.0;q-=3;for(int i=1;i<=3;++i){Set.insert(str[i]);}while(q--){scanf("%d%lf%lf",&oper,&a.x,&a.y);if(oper==1){Insert(a);}else{if(Query(a)) printf("YES\n");else printf("NO\n");}}
}

好了,动态凸包就到这里啦,什么??删除怎么办??不会啊,凉了,别写了,遇到了困难,就不做了,睡大觉!(雾雾雾

其实删除点的话,反过来看就是插入了,比如,还是上面的那张图:

我们一开始有1,2,A,B,C五个点构成了一个AB21凸包。第一次删掉点1,第二次删掉点2,最后剩一个ABC凸包。这反过来看。

最后有一个ABC凸包就是删掉点2的状态,加上点2就是删掉点1的状态,加上点1就是最初的状态,这样反过来求救可以转化成加点的问题了。

好了,赶紧做一道试试:

洛谷-P2521-[HAOI2011]防线修建(离线查找+平衡树维护动态凸包)

题目链接:https://www.luogu.org/problemnew/show/P2521

题目大意:中问题,就是我们上面说的那个情况。

思路:出题人真好啊,做出了一万个限制,限制了自己的数据范围,让我们感受到了来自大佬的关怀!赞美这个世界!

没错!样例就是我们举了两个例子的那个图片!我就是懒!

这里有和裸的维护动态凸包有所不同了,我们还要在维护凸包的周长,这个小问题:在ly大佬的调教下,终于学会了如何维护周长了嘤嘤嘤...

当判断出来这个点在凸包外后,比如凸包ABC,新增点2,我们首先减去AC边,添上2C边,然后开始维护左边的部分。最后只剩下一部分A2边,再维护右半部分,删掉BC,加上2B,直到维护完成。

没错!又是这个图!我就增加了两条紫色线!(理直气壮!

最后离线处理完反向输出就行了。

ACCode:

// luogu-judger-enable-o2
//#pragma comment(linker, "/STACK:1024000000,1024000000")#include<stdio.h>
#include<string.h>
#include<math.h>#include<map>
#include<set>
#include<deque>
#include<queue>
#include<stack>
#include<bitset>
#include<string>
#include<fstream>
#include<iostream>
#include<algorithm>
using namespace std;#define ll long long
#define Pair pair<int,int>
//#define max(a,b) (a)>(b)?(a):(b)
//#define min(a,b) (a)<(b)?(a):(b)
#define clean(a,b) memset(a,b,sizeof(a))// 水印
//std::ios::sync_with_stdio(false);
//  register
const int MAXN=1e5+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const int MOD=1e9+7;
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 int operator == (const Point &a,const Point &b){if(fabs(a.x-b.x)<EPS&&fabs(a.y-b.y)<EPS) return 1;return 0;}
};
struct V {Point start,end;double ang;V(Point _start=Point(0,0),Point _end=Point(0,0),double _ang=0.0) {start=_start;end=_end;ang=_ang;}friend V operator + (const V &a,const V &b) {return V(a.start+b.start,a.end+b.end);}friend V operator - (const V &a,const V &b) {return V(a.start-b.start,a.end-b.end);}
};
Point Basic,Dots[MAXN];
set<Point> Set;
Pair Ask[MAXN];
int Vis[MAXN];
double ans[MAXN],ansl;double Distance(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=Distance(a,Point(0,0)),Len2=Distance(b,Point(0,0));if(fabs(Ang1-Ang2)<EPS) return Len1<Len2;return Ang1<Ang2;
}
set<Point>::iterator Pre(set<Point>::iterator it){if(it==Set.begin()) it=Set.end();return --it;
}
set<Point>::iterator Nxt(set<Point>::iterator 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);ansl-=Distance(*(Nxt(Set.find(p))),*(Pre(Set.find(p))));ansl+=Distance(*(Set.find(p)),*(Pre(Set.find(p))));ansl+=Distance(*(Set.find(p)),*(Nxt(Set.find(p))));set<Point>::iterator it=Pre(Set.find(p));while(Set.size()>3&&((p- *(it))^(*(it)- *(Pre(it))))>-EPS){ansl-=Distance(*(it),*(Nxt(it)));ansl-=Distance(*(it),*(Pre(it)));ansl+=Distance(*(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-=Distance(*(it),*(Nxt(it)));ansl-=Distance(*(it),*(Pre(it)));ansl+=Distance(*(Nxt(it)),*(Pre(it)));Set.erase(it);it=Nxt(Set.find(p));}
//  cout<<"erase last"<<endl;
//  for(it=Set.begin();it!=Set.end();++it){
//      cout<<(*(it)).x<<" "<<(*(it)).y<<endl;
//  }cout<<"end Step"<<endl;
}
int main(){double n,x,y;scanf("%lf%lf%lf",&n,&x,&y);Basic=Point(0,0);Basic=Basic+Point(n,0);Basic=Basic+Point(x,y);Basic.x/=3.0;Basic.y/=3.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){Ask[i]=make_pair(2,0);}else{scanf("%d",&Index);Ask[i]=make_pair(1,Index);Vis[Index]=1;}}Set.insert(Point(0,0));Set.insert(Point(n,0));Set.insert(Point(x,y));ansl=Distance(Point(x,y),Point(0,0))+Distance(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(Ask[i].first==2){ans[k++]=ansl;//printf("%lf\n",ans[k++]);}else{Insert(Dots[Ask[i].second]);}}for(int i=k-1;i>=0;--i){printf("%.2lf\n",ans[i]);}
}
/*
8
1 0 0
1 2 0
1 2 2
2 1 0
1 0 2
2 1 1
2 2 1
2 20 -1*/

什么??!!碰见强制在线的情况怎么办??当然是发呆了,要不就是抱大腿,偷代码??这辈子都不会偷代码的

最后也没见到完整的平衡树??都说了太麻烦了!(其实是BUG没有调出来,还是太菜了啊)

凸包问题--动态凸包(平衡树维护)相关推荐

  1. 【bzoj2300】【Luogu P2521】 [HAOI2011]防线修建 动态凸包,平衡树,Set

    一句话题意:给你一个凸包,每次可以插入一个点或者询问周长. 动态凸包裸题嘛,用\(Set\)实现.最初每个点坐标做乘三处理,便于取初始三角形的重心作为凸包判定原点. #include <bits ...

  2. CF70D Professor's task(动态凸包)

    题面 两种操作: 1 往点集S中添加一个点(x,y); 2 询问(x,y)是否在点集S的凸包中. 数据保证至少有一个2操作, 保证刚开始会给出三个1操作, 且这三个操作中的点不共线. 题解 动态凸包板 ...

  3. [NOI2007]货币兑换Cash(DP+动态凸包)

    第一次打动态凸包维护dp,感觉学到了超级多的东西. 首先,set是如此的好用!!!可以通过控制一个flag来实现两种查询,维护凸包和查找斜率k 不过就是重载运算符和一些细节方面有些恶心,90行解决 后 ...

  4. CGAL笔记之凸包算法—3D凸包

    CGAL笔记之凸包算法-3D凸包 1 介绍 2 静态凸壳结构 2.1 特性类 2.1.1 示例 2.1.2 低维结果示例 2.2 极值点 2.3 半空间交集 2.3.1 例子 2.4 凸性检查 3 动 ...

  5. 上凸包和下凸包_使用凸包聚类

    上凸包和下凸包 I recently came across the article titled High-dimensional data clustering by using local af ...

  6. 微信小程序菜品做法展示数据库设计_微信小程序结合后台数据管理实现商品数据的动态展示、维护...

    微信小程序给我们提供了一个很好的开发平台,可以用于展现各种数据和实现丰富的功能,本篇随笔介绍微信小程序结合后台数据管理实现商品数据的动态展示.维护,介绍如何实现商品数据在后台管理系统中的维护管理,并通 ...

  7. [BZOJ1492] [NOI2007]货币兑换Cash 斜率优化+cdq/平衡树维护凸包

    1492: [NOI2007]货币兑换Cash Time Limit: 5 Sec  Memory Limit: 64 MB Submit: 5907  Solved: 2377 [Submit][S ...

  8. Codeforces 70D 动态凸包 (极角排序 or 水平序)

    题目链接:http://codeforces.com/problemset/problem/70/D 本题关键:在log(n)的复杂度内判断点在凸包 或 把点插入凸包 判断:平衡树log(n)内选出点 ...

  9. [WIKIOI1298]凸包周长[裸凸包]

    http://www.wikioi.com/problem/1298/ 这是题目. 是一道裸的凸包算法题,但是我一开始还是写WA了.先贴代码 1 #include <cstdio> 2 # ...

最新文章

  1. plsql创建中文表头_如何使用快捷键来提升Excel斜线表头绘制速度,照着学就行了...
  2. 那些功能逆天,却鲜为人知的pandas骚操作
  3. HAproxy - 铁钉 - 51CTO技术博客
  4. ViewController的生命周期分析和使用
  5. ~~朴素dijkstra算法
  6. Bailian3858 和数【暴力+集合】
  7. 智慧型数据中心露峥嵘
  8. 计算机实验室场地报告,实验室申请报告.doc
  9. 【Axure报错】-Unable to connect to Axure Share. Please make sure you have an internet connection and try
  10. threejs 管子_使用webgl(three.js)搭建一个3D智慧园区、3D建筑,3D消防模拟,web版3D,bim管理系统——第四课...
  11. Python爬虫 带你一键爬取LOL英雄皮肤壁纸
  12. 怀化市2021年高考查询成绩,2021年怀化高考状元是谁分数多少分,历年怀化高考状元名单...
  13. MFC的PNG图片按钮
  14. A 暴力搜索 剪枝是关键
  15. 临床试验数据的标准化之路,究竟还有多远?
  16. PHP商城笔记137 —— 商城项目知识点
  17. Python小案例(六)通过熵权法计算指标权重
  18. 不会画架构图的程序员不是好的技术扛把子,手把手练起来~
  19. YOLOv5实战之PCB板缺陷检测
  20. 大象医生.Net 版消息系统架构

热门文章

  1. 游戏开发校招面试全记录(大四篇)
  2. 从程序员到项目经理(一):为什么要当项目经理
  3. 分支语句(if,switch)
  4. 【五月集训】—— 汇聚星球,算法锤炼,集中一点,登峰造极
  5. java仙侠回合制单机游戏_‎永生劫-单机仙侠回合制RPG游戏 on the App Store
  6. Proteus中模拟CD40110操作及555组合成计数器
  7. ppt生成eps文件_如何将AI/EPS格式文件转为ppt格式
  8. 程序人生:每天工作的第一个小时,做什么?
  9. 水生生物学类毕业论文文献包含哪些?
  10. SQL中的sql%rowcount