2018清华集训(YL联考)9.9 T2 triangle

题目大意:

以下所有的东西都在第一象限。

给出n个点,和m个以原点为顶点的三角形,对每一个三角形询问是否有点在三角形内。

1 <= n <= 100000

题解:

把三角形看成原点出去的两个向量。

首先一个点要在三角形内,一定要在这两个向量的夹角间,并且要在三角形另外两个顶点形成的直线下。

第一个限制非常好搞,只需要把所有点极角排序,那么合法的点就在一个区间内了。

对于这个区间内的点,建一个下凸壳,我们只需要取凸壳上的直线斜率最大且小于等于这个三角形另外两个端点形成直线的斜率的那条直线对应的两个端点判断即可。

为什么呢?

可以把这条三角形端点形成的直线旋转成x轴,然后我们知道下凸壳的直线斜率是递减的,旋转坐标系后斜率也是递增的。

凸壳在旋转坐标系以后可以理解为先向下走,再向上走,所以我们只需要找到向上走的那个临界点就是最低点了。

于是用线段树维护凸壳,查询时二分。

复杂度 O(n log n+qlog2n) O ( n   l o g   n + q l o g 2 n )

Code:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define i0 i + i
#define i1 i + i + 1
using namespace std;

const int N = 1e5 + 5;

struct P {
    int x, y;
    P(int _x = 0, int _y = 0) {x = _x, y = _y;}
} a[N], b, c, t[N];

P operator -(P a, P b) {return P(a.x - b.x, a.y - b.y);}
ll operator ^(P a, P b) {return (ll) a.x * b.y - (ll) a.y * b.x;}
bool operator <(P a, P b) {return (a ^ b) > 0;}

int n, q, ans;

int st[N * 4], en[N * 4], d[N * 20], d0;
int pl, pr, z0;

void bl(int i, int x, int y) {
    if(x == y) {
        d[++ d0] = x;
        st[i] = en[i] = d0;
        return;
    }
    int m = x + y >> 1; bl(i0, x, m); bl(i1, m + 1, y);
    st[i] = d0 + 1;
    fo(j, st[i0], en[i0]) d[++ d0] = d[j];
    fo(j, st[i1], en[i1]) {
        while(d0 > st[i] && ((a[d[j]] - a[d[d0]]) ^ (a[d[d0]] - a[d[d0 - 1]])) <= 0) d0 --;
        d[++ d0] = d[j];
    }
    en[i] = d0;
}

void fi(int i, int x, int y) {
    if(y < pl || x > pr) return;
    if(x >= pl && y <= pr) {
        int p = st[i];
        for(int l = st[i], r = en[i] - 1; l <= r; ) {
            int m = l + r >> 1;
            if(((c - b) ^ (a[d[m + 1]] - a[d[m]])) >= 0)
                p = m, l = m + 1; else r = m - 1;
        }
        if(((c - b) ^ (a[d[p]] - b)) >= 0) ans = 1;
        if(p < en[i] && ((c - b) ^ (a[d[p + 1]] - b)) >= 0) ans = 1;
        if(p > st[i] && ((c - b) ^ (a[d[p - 1]] - b)) >= 0) ans = 1;
        return;
    }
    int m = x + y >> 1; fi(i0, x, m); fi(i1, m + 1, y);
}

int main() {
    scanf("%d %d", &n, &q);
    fo(i, 1, n) scanf("%d %d", &a[i].x, &a[i].y);
    sort(a + 1, a + n + 1);
    bl(1, 1, n);
    fo(ii, 1, q) {
        scanf("%d %d %d %d", &b.x, &b.y, &c.x, &c.y);
        if(c < b) swap(b, c);
        pl = lower_bound(a + 1, a + n + 1, b) - a, pr = upper_bound(a + 1, a + n + 1, c) - a - 1;
        ans = 0; fi(1, 1, n);
        putchar(ans ? 'Y' : 'N'); putchar('\n');
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值