sgu253:Theodore Roosevelt(扫描线)

题目大意:
       给出 n(n105) 个点的凸包以及另外 m(m105) 个点,以及一个不大于 m 的非负整数k,如果至少有 k 个点在凸包内,输出YES,否则 NO

分析:
       想了半天才想到是扫描线…
       把所有的点排序,从左往右扫,记录当前扫描线处的凸包上下界限,判断点是否在这个范围内即可。

AC code:

#include <cstdio>
#include <cmath>
#include <map>
#include <algorithm>
#define cx first
#define cy second
#define mp make_pair
#define eps 1e-8
#define is_zero(p) ((p) >= -eps && (p) <= eps)
typedef double DB;
using namespace std;

const int MAXN = 1e5+9;
const int MAXM = 1e5+9;

int n, m, k;
map< pair<int,int>,int > Map;
struct pot
{
    DB x, y;
    pot(DB tx=0, DB ty=0):x(tx),y(ty){}
    friend bool operator < (const pot &a, const pot &b)
    {
        return a.x < b.x-eps || (is_zero(a.x-b.x) && a.y < b.y-eps);
    }
}cvx[MAXN], other[MAXN];
int pre[MAXN][2], suf[MAXN][2];

pair<pot, int> node[MAXN+MAXM];

int sign(DB x)
{
    if(x < -eps) return -1;
    else return x > eps;    
}

DB fx(const pot &a, const pot &b, DB p)
{
    DB ret;
    if(is_zero(b.x-a.x)) return 1e10;
    ret = (p-a.x)/(b.x-a.x)*(b.y-a.y)+a.y;
    return ret;
}

void make_pre(int l, int r)
{
    int now = r;
    while(now != l)
    {
        pre[now][0] = now%n+1;
        suf[now%n+1][0] = now;
        now = now%n+1;  
    }
    now = r;
    while(now != l)
    {
        pre[now][1] = (now-2+n)%n+1;
        suf[(now-2+n)%n+1][1] = now;
        now = (now-2+n)%n+1;
    }
}

int main()
{
    #ifndef ONLINE_JUDGE
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    #endif

    scanf("%d%d%d", &n, &m, &k);
    int ru = 0, ld = 0, tot = 0;
    for(int i = 1; i <= n; ++i)
    {
        int x, y;
        scanf("%d%d", &x, &y);
        cvx[i] = pot(x, y);
        node[++tot] = mp(cvx[i], i);
        Map[mp(x, y)] = i;
        if(!ru || cvx[ru] < cvx[i]) ru = i;
        if(!ld || cvx[i] < cvx[ld]) ld = i;
    }
    make_pre(ld, ru);

    int sum = 0;
    for(int i = 1; i <= m; ++i)
    {
        int x, y;
        scanf("%d%d", &x, &y);
        other[i] = pot(x, y);
        if(Map.find(mp(x, y)) != Map.end()) sum++;
        else node[++tot] = mp(other[i], 0);
    }
    sort(node+1, node+n+m+1);

    bool in = false;
    int up, down;
    for(int i = 1; i <= n+m; ++i)
    {
        int id = node[i].cy;
        pot now = node[i].cx;
        if(!id)
        {
            if(in)
            {
                DB h1 = fx(cvx[up], cvx[suf[up][0]], now.x), h2 = fx(cvx[down], cvx[suf[down][1]], now.x);
                if(h1 > 1e9) {if(sign(cvx[suf[up][0]].y-now.y) >= 0) sum++;}
                else if(h2 > 1e9) {if(sign(cvx[suf[down][1]].y-now.y) >= 0) sum++;}
                else if(sign(now.y-h2) >= 0 && sign(h1-now.y) >= 0) sum++;
            }
        }
        else 
        {
            if(id == ld) in = true, up = down = id;
            else
            {
                if(id == ru) break;
                if(pre[id][0] == up) up = id;
                else down = id;
            }
        }
    }
    if(sum >= k) puts("YES");
    else puts("NO");

    #ifndef ONLINE_JUDGE
    fclose(stdin);
    fclose(stdout);
    #endif
    return 0;   
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值