凸包

凸包,即在平面上能包含给定所有点的最小凸多边形。它可以用最小的周长围住所有的点。

直接来到怎么求凸包。我们常用的有按照极角排序的Graham扫描法和按直角坐标排序的Andrew算法,两种算法的时间复杂度都是\(O(n\log n)\),且瓶颈都在排序。其中,Andrew算法由于常数更小(不需要求极角)而更常用。下面是Andrew算法的步骤:

首先把所有点按照横坐标为第一关键字,纵坐标为第二关键字排序。

我们发现排序之后,最小的和最大的元素显然在凸包上。而且在凸多边形上,我们从一个点出发,逆时针走,轨迹总是一路向左,如果该点和下一个点的轨迹向右,则该点一定不在凸包上。这和我们之前斜率优化中维护一个凸包是一样的,所以类似地,我们可以用一个单调栈维护凸包。

由于我们排序的顺序,我们先正序枚举点求出下凸壳,然后倒序枚举点求出上凸壳。每次类似斜率优化,看下一个点在栈顶的两个点组成的直线的哪一边(也就是叉积和\(0\)的关系),在左边则进栈,反之当前点退栈。注意这样做我们凸包点集的第一个和最后一个元素都是原点集的第一个元素,要减掉。最后我们就得到了按逆时针方向排序的凸包。

上个代码。

bool cmp(node a,node b){return fabs(a.x-b.x)<=eps?a.y<b.y:a.x<b.x;}
void tub(node a[],int n,node q[]){
    if(n==1){
        q[1]=q[2]=a[1]=1;return;
    }
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=n;q[++m]=a[i++]){
        while(m>1&&(q[m-1]-q[m]^q[m-1]-a[i])<=0)m--;//栈顶两个元素做叉积 判断是否成为凸包
    }
    int tmp=m;
    for(int i=n-1;i;q[++m]=a[i--]){
        while(m>tmp&&(q[m-1]-q[m]^q[m-1]-a[i])<=0)m--;//同理 倒序扫描
    }
    m--;//我们最后第一个和最后一个元素都是a[1] 要减一
}

动态凸包

用 set 或者 map 什么的维护凸包,每次插入一个点的时候判断两边的点能否删除。按照坐标排序,然后维护一个上凸包一个下凸包。显然是不可以可持久化的。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <vector>
#include <map>
#define int long long
using namespace std;
const double eps=1e-18;
map<int,int>top,down;
struct node{
    int x,y;
    int operator^(const node &s)const{
        return x*s.y-y*s.x;
    }
};
bool checktop(int x,int y){
    map<int,int>::iterator it=top.lower_bound(x);
    if(it==top.end())return false;
    if(it->first==x)return y<=it->second;
    if(it==top.begin())return false;
    map<int,int>::iterator ret=it;
    --ret;
    node a={it->first-ret->first,it->second-ret->second},b={x-ret->first,y-ret->second};
    return (a^b)<=0;
}
bool checkdown(int x,int y){
    map<int,int>::iterator it=down.lower_bound(x);
    if(it==down.end())return false;
    if(it->first==x)return y>=it->second;
    if(it==down.begin())return false;
    map<int,int>::iterator ret=it;
    --ret;
    node a={it->first-ret->first,it->second-ret->second},b={x-ret->first,y-ret->second};
    return (a^b)>=0;
}
bool deltop(map<int,int>::iterator it){
    if(it==top.begin())return false;
    map<int,int>::iterator ret=it,tmp=it;
    --ret;++tmp;
    if(tmp==top.end())return false;
    node a={it->first-ret->first,it->second-ret->second},b={tmp->first-ret->first,tmp->second-ret->second};
    if((a^b)>=0){
        top.erase(it);return true;
    }
    return false;
}
bool deldown(map<int,int>::iterator it){
    if(it==down.begin())return false;
    map<int,int>::iterator ret=it,tmp=it;
    --ret;++tmp;
    if(tmp==down.end())return false;
    node a={it->first-ret->first,it->second-ret->second},b={tmp->first-ret->first,tmp->second-ret->second};
    if((a^b)<=0){
        down.erase(it);return true;
    }
    return false;
}
void instop(int x,int y){
    if(checktop(x,y))return;
    top[x]=y;
    map<int,int>::iterator it=top.find(x),ret=it;
    if(it!=top.begin()){
        --ret;
        while(deltop(ret++))--ret;
    }
    if(++ret!=top.end()){
        while(deltop(ret--))++ret;
    }
}
void insdown(int x,int y){
    if(checkdown(x,y))return;
    down[x]=y;
    map<int,int>::iterator it=down.find(x),ret=it;
    if(it!=down.begin()){
        --ret;
        while(deldown(ret++))--ret;
    }
    if(++ret!=down.end()){
        while(deldown(ret--))++ret;
    }
}
signed main(){
    int tim;scanf("%lld",&tim);
    while(tim--){
        int od,x,y;scanf("%lld%lld%lld",&od,&x,&y);
        if(od==1){
            instop(x,y);insdown(x,y);
        }
        else{
            if(checktop(x,y)&&checkdown(x,y))puts("YES");
            else puts("NO");
        }
    }
    return 0;
}
posted @ 2022-09-03 19:48  gtm1514  阅读(100)  评论(0编辑  收藏  举报