「JSOI2018」战争

本文详细解析了JSOI2018比赛中的“战争”题目,通过使用凸包和闵可夫斯基和的概念来解决向量与凸包相交的问题。算法首先求出两个点集的凸包,然后计算它们的闵可夫斯基和,最终判断特定向量是否位于新的凸包内部。

「JSOI2018」战争

解题思路

我们需要每次求给一个凸包加上一个向量后是否与另外一个凸包相交,也就是说是否存在
\[ b\in B,(b+w)\in A \]
这里 \(A, B\) 表示凸包内部的点集,可以转化一步变成
\[ a\in A,b \in B,b+w=a \\ w =a -b \]
那相当于对 \(A,(-B)\) 作闵可夫斯基和,判断 \(w\) 是否在新的凸包内部,把新的凸包划分成三角区域,让 \(w\) 和原点做一条向量,二分一下在哪个区域然后判断一下在区域内部还是外部就可以了,复杂度 \(\mathcal O(n \log n)\)


code
/*program by mangoyang*/ 
#include<bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
    int ch = 0, f = 0; x = 0;
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
    for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
    if(f) x = -x;
}
const int N = 1000005;
struct P{
    ll x, y;
    friend P operator + (P a, P b){ return (P){a.x + b.x, a.y + b.y}; }
    friend P operator - (P a, P b){ return (P){a.x - b.x, a.y - b.y}; }
    friend ll operator * (P a, P b){ return a.x * b.y - b.x * a.y; }
    inline ll dis(){ return x * x + y * y; }
}A[N], B[N], C[N], s1[N], s2[N], st[N], O;
int n, m, q;
inline bool cmp1 (P A, P B){ 
    return A.y != B.y ? A.y < B.y : A.x < B.x;
}
inline bool cmp2 (P A, P B){ 
    //叉积一样按照离原点距离排,防止较远的点被近的点日掉
    ll res = (A - O) * (B - O);
    return res ? res > 0 : (A - O).dis() < (B - O).dis();
}
inline int convex(P *A, int len){ 
    //求点集 A 的凸包并返回凸包大小
    sort(A + 1, A + len + 1, cmp1); O = A[1];
    sort(A + 2, A + len + 1, cmp2);
    int top = 1; st[top] = A[1];
    for(int i = 2; i <= len; i++){
        while(top > 1 && (st[top] - st[top-1]) * (A[i] - st[top-1]) <= 0) top--;
        st[++top] = A[i];
    }
    for(int i = 1; i <= top; i++) A[i] = st[i];
    return top;
}
inline int inconvex(P x, P *A, int len){ 
    //判断点 x 是否在大小为len的凸包 A 里,二分找到向量所在的三角区域
    O = A[1];
    if((x - O) * (A[2] - O) > 0 || (x - O) * (A[len] - O) < 0) return 0;
    int pos = lower_bound(A + 2, A + len + 1, x, cmp2) - A - 1;
    return (x - A[pos]) * (A[pos%len+1] - A[pos]) <= 0;

}
inline int Minkowski(P *A, P *B, P *C, int n, int m){
    //将大小为 n, m 的凸包 A, B 的闵可夫斯基和存在 C 中,并返回凸包大小
    int tot1 = 0, tot2 = 0;
    for(int i = 1; i < n; i++) s1[++tot1] = A[i+1] - A[i];
    s1[++tot1] = A[1] - A[n];
    for(int i = 1; i < m; i++) s2[++tot2] = B[i+1] - B[i];
    s2[++tot2] = B[1] - B[m];
    int p1 = 1, p2 = 1, tot = 1; C[tot] = A[1] + B[1];
    for(; p1 <= n && p2 <= m; tot++)
        C[tot+1] = C[tot] + (s1[p1] * s2[p2] >= 0 ? s1[p1++] : s2[p2++]);
    for(; p1 <= n; p1++, tot++) C[tot+1] = C[tot] + s1[p1];
    for(; p2 <= m; p2++, tot++) C[tot+1] = C[tot] + s2[p2];
    return tot = convex(C, tot);
}
int main(){
    read(n), read(m), read(q);
    for(int i = 1; i <= n; i++) read(A[i].x), read(A[i].y);
    n = convex(A, n);
    for(int i = 1; i <= m; i++)
        read(B[i].x), read(B[i].y), B[i].x = -B[i].x, B[i].y = -B[i].y;
    m = convex(B, m);
    int len = Minkowski(A, B, C, n, m);
    while(q--){
        ll x, y; read(x), read(y);
        printf("%d\n", inconvex((P){x, y}, C, len));
    }
    return 0;
}

转载于:https://www.cnblogs.com/mangoyang/p/10529096.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值