「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;
}