洛谷传送门
BZOJ传送门
解析;
首先可以很显然的发现,一个部落能够控制的点就是它的点集形成的凸包中的点。
先求两个凸包 A , B A,B A,B。
首先将问题转化成数学语言:
给出一个向量 v ⃗ = ( d x , d y ) \vec{v}=(dx,dy) v=(dx,dy),询问是否 ∃ b ∈ B , b + v ⃗ ∈ A \exist b\in B,b+\vec{v}\in A ∃b∈B,b+v∈A
进行一点小转化: a = b + v ⃗ a − b = v ⃗ a=b+\vec{v}\\a-b=\vec{v} a=b+va−b=v
就是说是否存在 a , b a,b a,b两个点向量,使得它们之间的向量等于询问向量。
好像仍然不是很清楚,所以我们将 B B B凸包横纵坐标全部乘上 − 1 -1 −1,得到我们要询问的东西:
a + b = v ⃗ a+b=\vec{v} a+b=v
是否存在 A A A凸包中的点,使得其以某个凸包 B B B中的点向量为方向平移后等于询问向量。
介绍一个点集之间的运算,Minkowski Sum:
Minkowski Sum 的运算对象是两个点集,运算结果是一个点集,记两个点集 A , B A,B A,B的Minkowski Sum为点集 C C C,满足 C = { a + b ∣ a ∈ A , b ∈ B } C=\{a+b|a\in A,b\in B\} C={a+b∣a∈A,b∈B}
显然刚才这个问题就是问 v ⃗ \vec{v} v是否在 A , B A,B A,B凸包的Minkowski Sum的凸包中。证明如果不画图的有点麻烦,然而我这种手残党怎么可能画图,所以请各位自行画图理解。
要判断点是否在凸包内很简单,极角二分加特判就行了。
所以现在问题是怎么快速地求两个凸包的Minkowski Sum的凸包。
如果直接按照定义构造点集 C C C之后求凸包的话,复杂度是 O ( n m ( log n + log m ) ) O(nm(\log n+\log m)) O(nm(logn+logm))显然会 g g gg gg。
但是我们不是要求Minkowski Sum,而是求凸包,考虑有没有什么性质可以利用。
连图都不用画我们就知道, A A A的最左点加上 B B B的最右点是什么用都没有的。
换句话说,我们只需要在每个方向上选择 A , B A,B A,B最靠近这个方向的点来构成凸包就行了。
由于凸包上的斜率是单调的,所有我们可以极角来进行拓扑排序,当然,实际操作不用极角,用叉积就行了。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define cs const
namespace IO{
inline char get_char(){
static cs int Rlen=1<<20|1;
static char buf[Rlen],*p1,*p2;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
inline int getint(){
re char c;
re bool f=0;
while(!isdigit(c=gc()))if(c=='-')f=1;re int num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return f?-num:num;
}
}
using namespace IO;
cs double eps=1e-6;
struct Point{
double x,y;
Point(){}
Point(cs double &_x,cs double &_y):x(_x),y(_y){}
friend Point operator+(cs Point &a,cs Point &b){return Point(a.x+b.x,a.y+b.y);}
friend Point operator-(cs Point &a,cs Point &b){return Point(a.x-b.x,a.y-b.y);}
friend Point operator*(cs Point &a,cs double &b){return Point(a.x*b,a.y*b);}
friend double operator*(cs Point &a,cs Point &b){return a.x*b.y-a.y*b.x;}
friend double dot(cs Point &a,cs Point &b){return a.x*b.x+a.y*b.y;}
double norm(){return sqrt(dot(*this,*this));}
};
inline int sign(double x){
return fabs(x)<eps?0:(x>0?1:-1);
}
inline int convex_hull(Point *p,int n){
for(int re i=2;i<=n;++i)
if(p[i].x<p[1].x||(sign(p[i].x-p[1].x)==0&&p[i].y<p[1].y))swap(p[i],p[1]);
sort(p+2,p+n+1,[&](cs Point &a,cs Point &b){
if(sign((a-p[1])*(b-p[1])))return sign((a-p[1])*(b-p[1]))>0;
return (a-p[1]).norm()<(b-p[1]).norm();
});
int m=1;
for(int re i=2;i<=n;++i){
while(m>=2&&sign((p[i]-p[m-1])*(p[m]-p[m-1]))>=0)--m;
p[++m]=p[i];
}
return m;
}
cs int N=100005;
int n,m,q,cnt;
Point p1[N],p2[N],p[N<<1],v1[N],v2[N];
inline void Minkowski(){
p1[n+1]=p1[1];for(int re i=1;i<=n;++i)v1[i]=p1[i+1]-p1[i];
p2[m+1]=p2[1];for(int re i=1;i<=m;++i)v2[i]=p2[i+1]-p2[i];
p[1]=p1[1]+p2[1];cnt=1;
re int q1=1,q2=1;
while(q1<=n&&q2<=m)++cnt,p[cnt]=p[cnt-1]+((sign(v1[q1]*v2[q2])>=0)?v1[q1++]:v2[q2++]);
while(q1<=n)++cnt,p[cnt]=p[cnt-1]+v1[q1++];
while(q2<=m)++cnt,p[cnt]=p[cnt-1]+v2[q2++];
while(cnt>1&&sign((p[cnt]-p[cnt-1])*(p[1]-p[cnt-1]))<=0)--cnt;
}
inline bool in(cs Point &q){
Point v=q-p[1];
if(sign(v*(p[cnt]-p[1]))<0||v*(p[2]-p[1])>0)return false;
int pos=lower_bound(p+2,p+cnt+1,q,[&](cs Point &a,cs Point &b){
if(sign((a-p[1])*(b-p[1])))return sign((a-p[1])*(b-p[1]))>0;
return (a-p[1]).norm()<(b-p[1]).norm();
})-p-1;
return (q-p[pos])*(p[pos%cnt+1]-p[pos])<=0;
}
signed main(){
n=getint();m=getint();q=getint();
for(int re i=1;i<=n;++i)p1[i].x=getint(),p1[i].y=getint();
n=convex_hull(p1,n);
for(int re i=1;i<=m;++i)p2[i].x=-getint(),p2[i].y=-getint();
m=convex_hull(p2,m);
Minkowski();
while(q--){
int x=getint(),y=getint();
cout<<in(Point(x,y))<<"\n";
}
return 0;
}