「JSOI2018」战争

题目链接

题目大意

本题解中向量与点互通。

题目大概就是判断一个凸包\(B\)平移\(\overrightarrow v\)之后是否与凸包\(A\)有相交。相交即\(\exists b \in B, b + \overrightarrow v =a\in A\)

如果转换一下,就是\(\exists b \in B, a\in A, a - b = \overrightarrow v\)。等价于\(S=\{a-b,a\in A,b\in B\},\overrightarrow v \in S\)

闵可夫斯基和:对于点集\(A\)\(B\),定义他们的闵可夫斯基和\(A+B=\{a+b, a\in A, b \in B\}\)。更多的闵可夫斯基和相关和计算几何相关会在以后的博客中详细介绍,这里先挖个坑。

于是这道题就是求两个点集闵可夫斯基和的凸包,然后判断一个点是否在凸包内。而两个凸包的闵可夫斯基和的凸包可以在\(O(n+m)\)之内得到,于是这题就是\(O(q\log n +n\log n)\)

参考程序

#pragma GCC diagnostic error "-std=c++17"
#include <bits/stdc++.h>
#define LL long long
using namespace std;

const LL Maxn = 100010;
struct point {
    LL x, y;
    point() {};
    point( LL _x, LL _y ) : x( _x ), y( _y ) {}
    point operator + ( const point Other ) const {
        point Ans( x + Other.x, y + Other.y );
        return Ans;
    }
    point operator - ( const point Other ) const {
        point Ans( x - Other.x, y - Other.y );
        return Ans;
    }
    bool operator == ( const point Other ) const {
        return x == Other.x && y == Other.y;
    }
};
point Standard;
LL Cross( point A, point B ) {
    return A.x * B.y - B.x * A.y;
}
LL Cross( point A, point B, point C ) {
    return ( A.x - C.x ) * ( B.y - C.y ) - ( B.x - C.x ) * ( A.y - C.y );
}
bool Cmp1( point X, point Y ) {
    return X.y < Y.y || X.y == Y.y && X.x < Y.x;
}
bool Cmp2( point X, point Y ) {
    LL T = Cross( X, Y, Standard );
    return T > 0LL || T == 0LL && X.x - Standard.x < Y.x - Standard.x;
}
LL Stack[ Maxn ], Num;
struct pointSet {
    LL Size;
    point A[ Maxn << 2 ];
    inline void Print() {
        printf( "Size : %lld\n", Size );
        for( int i = 1; i <= Size; ++i ) printf( "%lld %lld\n", A[ i ].x, A[ i ].y );
        return;
    }
    void Collet() {
        sort( A + 1, A + Size + 1, Cmp1 );
        Standard = A[ 1 ];
        sort( A + 2, A + Size + 1, Cmp2 );
        Num = 2; Stack[ 1 ] = 1; Stack[ 2 ] = 2;
        for( LL i = 3; i <= Size; ++i ) {
            for( ; Num > 1 && Cross( A[ i ], A[ Stack[ Num ] ], A[ Stack[ Num - 1 ] ] ) >= 0LL; --Num );
            Stack[ ++Num ] = i;
        }
        for( LL i = 1; i <= Num; ++i ) A[ i ] = A[ Stack[ i ] ]; Size = Num;
        return;
    }
};
LL q;
pointSet A, B, C;
point T;

bool Test( LL Left, LL Right ) {
    if( Left + 1 == Right ) {
        return Cross( T, C.A[ Left ], C.A[ Right ] ) >= 0;
    }
    LL Mid = ( Left + Right ) >> 1;
    if( Cross( C.A[ Mid ], T, C.A[ 1 ] ) >= 0 ) return Test( Mid, Right ); else return Test( Left, Mid );
}

int main() {
    scanf( "%lld%lld%lld", &A.Size, &B.Size, &q );
    for( LL i = 1; i <= A.Size; ++i ) scanf( "%lld%lld", &A.A[ i ].x, &A.A[ i ].y );
    for( LL i = 1; i <= B.Size; ++i ) scanf( "%lld%lld", &B.A[ i ].x, &B.A[ i ].y );
    for( LL i = 1; i <= B.Size; ++i ) B.A[ i ].x = -B.A[ i ].x, B.A[ i ].y  = -B.A[ i ].y;
    A.Collet(); B.Collet();
    A.A[ ++A.Size ] = A.A[ 1 ]; B.A[ ++B.Size ] = B.A[ 1 ];
    C.Size = 0;
    C.A[ ++C.Size ] = A.A[ 1 ] + B.A[ 1 ];
    LL i1 = 2, i2 = 2;
    for( ; i1 <= A.Size && i2 <= B.Size; ) {
        if( Cross( A.A[ i1 ] - A.A[ i1 - 1 ], B.A[ i2 ] - B.A[ i2 - 1 ] ) >= 0LL ) {
            C.A[ C.Size + 1 ] = C.A[ C.Size ] + ( A.A[ i1 ] - A.A[ i1 - 1 ] );
            ++i1; ++C.Size;
        } else {
            C.A[ C.Size + 1 ] = C.A[ C.Size ] + ( B.A[ i2 ] - B.A[ i2 - 1 ] );
            ++i2; ++C.Size;
        }
    }
    for( ; i1 <= A.Size; ++i1 ) C.A[ C.Size + 1 ] = C.A[ C.Size ] + ( A.A[ i1 ] - A.A[ i1 - 1 ] ), ++C.Size;
    for( ; i2 <= B.Size; ++i2 ) C.A[ C.Size + 1 ] = C.A[ C.Size ] + ( B.A[ i2 ] - B.A[ i2 - 1 ] ), ++C.Size;
    Standard = C.A[ 1 ];
    for( LL i = 1; i <= q; ++i ) {
        scanf( "%lld%lld", &T.x, &T.y );
        if( Cross( C.A[ C.Size ], T, C.A[ 1 ] ) > 0LL || Cross( T, C.A[ 2 ], C.A[ 1 ] ) > 0LL ) {
            printf( "0\n" );
            continue;
        }
        if( T == C.A[ 1 ] || Test( 2, C.Size ) ) printf( "1\n" ); else printf( "0\n" );
    }
    return 0;
}

转载于:https://www.cnblogs.com/chy-2003/p/11265803.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值