Loading

P4557 [JSOI2018]战争

给出两个凸包。之后有q个询问问你第一个凸包加上给出的向量会不会与第二个凸包有交点。

这里假设A为第一个凸包的点集,B为第二个凸包的点击,p为移动的向量。

那么由题意可以得到: A + p = B, 将式子稍微变形可以得到这个移动向量的范围,即p = B - A

参考链接:https://www.cnblogs.com/xzyxzy/p/10229921.html

这里引入一个几何概念:

闵可夫斯基和:定义F(A, B)为闵可夫斯基和

F(A, B) = {(ai+bi, aj+bj)| Vai属于A, Vbi属于B}

通俗一点:就是将一个多边形的一个点定到另一个多边形上,之后定点绕着多边形移动所画出的区域就是。

图片来源于:上面的博客。

这里还需要一个二分判断点是否在凸包内的。

具体的思想就是叉积,两条线的叉积能确定线的相对位置,所以可以二分。

以下是代码:

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
typedef long long ll;

struct Point {
    ll x, y;
    Point(){x=y=0;}
    Point(ll _x, ll _y):x(_x),y(_y){}
    Point operator-(const Point& _p) const{return Point(x-_p.x, y-_p.y);}
    Point operator+(const Point& _p) const{return Point(x+_p.x, y+_p.y);}

    ll operator*(const Point& _p) const{return x * _p.x + y * _p.y;}
    ll operator^(const Point& _p) const{return x*_p.y - y*_p.x;}

    void input(){scanf("%lld%lld", &x, &y);}
    /*sort*/
    bool operator==(const Point& _p) const{return x==_p.x&&y==_p.y;}
    bool operator<(const Point& _p) const{return x==_p.x ? y<_p.y : x<_p.x;}

} A[N], B[N], C[N];

ll cro(Point a, Point b, Point c) {return (b-a) ^ (c-a);}
ll dot(Point a, Point b, Point c) {return (b-a) * (c-a);}

int ConvexHull(Point *p, int n, Point* ch, int flag=1) {
    sort(p, p+n);      //先比较x坐标,再比较y坐标
    if (flag) n = unique(p, p+n)-p;
    int m = 0;
    for (int i = 0; i < n; ++ i) {
        while (m > 1 && cro(ch[m-2], ch[m-1], p[i]) < flag) -- m;
        ch[m++] = p[i];
    }
    int temp = m;
    for (int i = n - 2; i >= 0; -- i) {
        while (m > temp && cro(ch[m-2], ch[m-1], p[i]) < flag) -- m;
        ch[m++] = p[i];
    }
    return m -= (n > 1);
}

//点在线段上(包含端点),在为 1 ,不在为 0
bool isPointOnSegment(Point P,Point A,Point B) {
    return cro(P, A, B) == 0 && dot(P, A, B) <= 0;  // 不包含端点时为 <0
}
//判断点在凸包内模板 O(logn)
//凸包为逆时针
//在边界,返回0,在内部,返回1,在外部,返回-1
int check(Point A, Point*p, int n ) {
    if (isPointOnSegment(A, p[0], p[1])) return 0; // 包含端点
    if (isPointOnSegment(A, p[0], p[n-1])) return 0;
    int l = 1, r = n - 2, mid;
    while (l <= r) {
        mid = l + r >> 1;
        ll a1 = cro(p[0], p[mid], A);
        ll a2 = cro(p[0], p[mid+1], A);
        if (a1 >= 0 && a2 <= 0) {
            ll tmp = cro(p[mid], p[mid+1], A);
            if (tmp > 0)return 1;
            if (tmp == 0) return 0;
            return -1;
        }
        else if (a1 < 0) r = mid - 1;
        else l = mid + 1;
    }
    return -1;
}

int Minkowski(Point *A, int n, Point *B, int m, Point *C){
    int t = 0, x = 1, y = 1;
    C[t++] = A[0] + B[0];
    A[n] = A[0], B[m] = B[0];
    while (x <= n && y <= m)
        if ( ((A[x]-A[x-1]) ^ (B[y]-B[y-1])) > 0)
            C[t] = C[t-1] + A[x] - A[x-1], t ++, x ++;
        else
            C[t] = C[t-1] + B[y] - B[y-1], t ++, y ++;
    while (x <= n) C[t] = C[t-1] + A[x] - A[x-1], t ++, x ++;
    while (y <= m) C[t] = C[t-1] + B[y] - B[y-1], t ++, y ++;
    return t - 1;
}

int main() {
//    freopen("in.txt", "r", stdin);
//    freopen("out.txt", "w", stdout);
    int n, m, q;
    scanf("%d%d%d", &n, &m, &q);
    for (int i = 0; i < n; ++ i) A[i].input();
    for (int i = 0; i < m; ++ i)
        B[i].input(), B[i].x *= -1, B[i].y *= -1;
    n = ConvexHull(A, n, C);
    memcpy(A, C,sizeof(Point)*(n));
    m = ConvexHull(B, m, C);
    memcpy(B, C,sizeof(Point)*(m));

    int p = Minkowski(A, n, B, m, C);

    for (int i = 0; i < q; ++ i) {
        Point temp; temp.input();
        puts(check(temp, C, p) == -1 ? "0" : "1");
    }

    return 0;
}
View Code

 这里有个小知识: 闵可夫斯基和凸包上的点等于向量种类数

posted @ 2021-03-09 16:44  ViKyanite  阅读(106)  评论(0编辑  收藏  举报