对于动态凸包添加的问题的一般做法:
如上图。我们知道,如果能够有一种数据结构可以维护一下凸包上的顶点与原点的角度,对于插入一个点now的时候,我们就可以二分出点的位置,然后找到比他角度小的上一个顶点pre以及比他角度大的下一个顶点next,通过判断now跟next,pre的叉积正负来判断点now是否在凸包内。
对于需要插入不在凸包中的点时,我们找出了pre,next之后,需要分别通过维护上凸包以及下凸包,把多余的点从平衡树中删掉。例如上图中,由于next_next可以被now“看见”,所以next需要被删掉,删掉next之后继续判断next_next是否需要删掉,直到不能够删掉为止。对于下凸包同样进行这样的操作。
由于我们使用atan2(y,x)这样的方式来维护极角序,不可避免的存在极角相同的情况,可能存在精度误差使得找出的pre,next不是真正意义上的pre,next,所以我们需要在插入前三个点的时候用随机数乘上三个点的坐标,然后以该坐标的加权平均数作为原点。具体可以看代码。
如果可以用long long,最好直接用long long表示点的坐标,因为用double的精度可能不够,而判断叉积正负的时候可以直接用原坐标来判断。
题目:
现在有两种操作:
1.把(x,y)加到凸包中
2.问(x,y)是否在凸包中
现在给出n个操作,对于操作二,输出
分析:
这题的数据比较强,所以强烈推荐做一下。
#include <set> #include <map> #include <cmath> #include <queue> #include <stack> #include <string> #include <vector> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long ll; typedef unsigned long long ull; #define debug puts("here") #define rep(i,n) for(int i=0;i<n;i++) #define rep1(i,n) for(int i=1;i<=n;i++) #define REP(i,a,b) for(int i=a;i<=b;i++) #define foreach(i,vec) for(unsigned i=0;i<vec.size();i++) #define pb push_back #define RD(n) scanf("%d",&n) #define RD2(x,y) scanf("%d%d",&x,&y) #define RD3(x,y,z) scanf("%d%d%d",&x,&y,&z) #define RD4(x,y,z,w) scanf("%d%d%d%d",&x,&y,&z,&w) #define All(vec) vec.begin(),vec.end() #define MP make_pair #define PII pair<int,int> /******** program ********************/ const double eps = 1e-8; double ox,oy; struct node{ int x,y; double angle; int op; friend bool operator < (node a,node b){ return a.angle-b.angle<0; } inline void rd(){ scanf("%d%d%d",&op,&x,&y); } inline void ch(){ angle = atan2(y-oy,x-ox); } inline void od(){ cout<<op<<" "<<x<<" "<<y<<" "<<atan2(y,x)<<endl; } }; set<node> s; inline node getPre(node now){ // 得到前驱 if(s.count(now)>0) return now; set<node>::iterator it = s.lower_bound(now ); if(it==s.begin()) it = s.end(); return *--it; } inline node getNext(node now){ // 得到后继 set<node>::iterator it = s.upper_bound( now ); if(it==s.end()) it = s.begin(); return *it; } inline ll det(node a,node b,node o){ return ll(a.x-o.x)*(b.y-o.y)-ll(b.x-o.x)*(a.y-o.y); } inline bool in(node now){ // 判断点是否在凸包内 if(s.size()<3) return false; node p = getPre(now); node n = getNext(now); return det(p,n,now)>=0; } inline void add(node now){ // 添加 if(in(now)) return; while(1){ node n = getNext(now); s.erase(n); node nn = getNext(now); if(det(now,nn,n)<0){ s.insert(n); break; } } while(1){ node p = getPre(now); s.erase(p); node pp = getPre(now); if(det(pp,now,p)<0){ s.insert(p); break; } } s.insert(now); } int main(){ #ifndef ONLINE_JUDGE freopen("sum.in","r",stdin); //freopen("sum.out","w",stdout); #endif int n; while(~RD(n)){ node a[5]; ox = oy = 0; double t[] = {0,0.49214632134, 0.2348329743213, 0.9854827427182}; double sum = 0; rep1(i,3){ a[i].rd(); ox += a[i].x*t[i]; oy += a[i].y*t[i]; sum += t[i]; } s.clear(); ox /= sum, oy /= sum; rep1(i,3){ a[i].ch(); s.insert(a[i]); } node now; n -= 3; while(n--){ now.rd(); now.ch(); if(now.op==1) add(now); else in(now)?puts("YES"):puts("NO"); } } return 0; }
先给出三个点,保证三个点不共线。
现在添加k个点,问添加第i个点时,现有的点所形成的凸包面积的两倍
分析:
动态凸包添加点计算面积的问题。
我们先判断点是否在凸包内。
不在的话,把三角形(next,pre,now)加上。
然后对于上半部分,判断next是否需要删掉,需要删掉的话,面积加上三角形(next,next_next,now),
继续判断下一个点。
对于下半部分进行同样的操作即可。
#include <set> #include <map> #include <cmath> #include <queue> #include <stack> #include <string> #include <vector> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long ll; typedef unsigned long long ull; #define debug puts("here") #define rep(i,n) for(int i=0;i<n;i++) #define rep1(i,n) for(int i=1;i<=n;i++) #define REP(i,a,b) for(int i=a;i<=b;i++) #define foreach(i,vec) for(unsigned i=0;i<vec.size();i++) #define pb push_back #define RD(n) scanf("%d",&n) #define RD2(x,y) scanf("%d%d",&x,&y) #define RD3(x,y,z) scanf("%d%d%d",&x,&y,&z) #define RD4(x,y,z,w) scanf("%d%d%d%d",&x,&y,&z,&w) #define All(vec) vec.begin(),vec.end() #define MP make_pair #define PII pair<int,int> /******** program ********************/ ll area; struct node{ ll x,y; double ang; node(){} node(ll _x,ll _y):x(_x),y(_y){} node(ll _x,ll _y,double _a):x(_x),y(_y),ang(_a){} void rd(){ int _x,_y; RD2(_x,_y); x = _x; y = _y; } void od(){ cout<<x<<" "<<y<<" "<<ang<<endl; } friend bool operator < (node a,node b){ return a.ang<b.ang; } friend node operator - (node a,node b){ return node(a.x-b.x,a.y-b.y); } }; set<node> s; ll det(node a,node b){ return a.x*b.y-a.y*b.x; } ll det(node a,node b,node o){ return det(a-o,b-o); } node getPre(node now){ if(s.count(now)>0) return now; set<node>::iterator it = s.lower_bound(now); if(it==s.begin()) it = s.end(); return *--it; } node getNext(node now){ set<node>::iterator it = s.upper_bound(now); if(it==s.end()) it = s.begin(); return *it; } bool in(node now){ node p = getPre(now); node n = getNext(now); return det(now,n,p)<=0; } void add(node now){ if(in(now)) return; area += abs( det(now,getNext(now),getPre(now)) ); while(true){ node p = getPre(now); s.erase(p); node pp = getPre(now); if(det(now,pp,p)>=0){ s.insert(p); break; } area += abs(det(now,pp,p)); } while(true){ node n = getNext(now); s.erase(n); node nn = getNext(now); if(det(now,nn,n)<=0){ s.insert(n); break; } area += abs(det(n,nn,now)); } s.insert(now); } int main(){ #ifndef ONLINE_JUDGE freopen("sum.in","r",stdin); //freopen("sum.out","w",stdout); #endif node a[5]; double t[] = {0.49214632134, 0.2348329743213, 0.9854827427182}; double sum = 0; double ox = 0 , oy = 0; rep(i,3){ a[i].rd(); ox += a[i].x*t[i]; oy += a[i].y*t[i]; sum += t[i]; } ox /= sum; oy /= sum; s.clear(); rep(i,3) s.insert(node(a[i].x,a[i].y,atan2(a[i].y-oy,a[i].x-ox))); area = abs( det(a[0],a[1],a[2]) ); int n; RD(n); int x,y; rep(i,n){ RD2(x,y); add( node(x,y,atan2(y-oy,x-ox)) ); printf("%I64d\n",area); } return 0; }