动态凸包问题

对于动态凸包添加的问题的一般做法:

 

  如上图。我们知道,如果能够有一种数据结构可以维护一下凸包上的顶点与原点的角度,对于插入一个点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的精度可能不够,而判断叉积正负的时候可以直接用原坐标来判断。

 

 CF 70 D. Professor's task

题目:

现在有两种操作:
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;
}

  

SUG 277 Heros

题目:

先给出三个点,保证三个点不共线。
现在添加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;
}

  

 

转载于:https://www.cnblogs.com/yejinru/p/3156048.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值