闵可夫斯基和简介
两个凸包 \(A,B\),分别有 \(n,m\) 个端点,记 \(a\in A\) 表示点 \(a\) 在多边形内(含边界),\(A\) 和 \(B\) 的闵可夫斯基和定义为(\(a+b\) 表示向量相加):
\[C=\{a+b|a\in A,b\in B\}
\]
使用反证法和肉眼观察尝试法不难得出:
- \(C\) 是凸包
- \(C\) 的边(共 \(n+m\) 条)是由原 \(A,B\) 的边一一平移得来
故:对 \(A,B\) 极角排序,得到它们的边集,并起来,按极角排序,按时针方向依次就是 \(C\) 的边集。
形状知道了,位置在哪里?
我的做法是求出 \(mnax,mnay,mnbx,mnby\),那么不难发现 \(mn(A+B)_{实际}x=mnax+mnbx,mn(A+B)_{实际}y=mnay+mnby\)。
练习:[JSOI2018]战争
\(\exists b,b+\overrightarrow x\in A\iff x\in A-B\)
所以要干两件事:
- 求 \(A\) 和 \(-B\) 的闵可夫斯基和 \(C\)
- 快速判断点是否在多边形内
第一件事我们已经知道了。
第二件事,我们可以把凸包 \(C\) 的任意一个端点当做原点 \(O\),\(\overrightarrow{OX}\) 在以 O 为原点、按照 atan2(逆时针)极角排序后的点集 \(C\) 中的后继 \(P\),以及 \(P\) 的前驱 \(Q\),利用 \(\overrightarrow{PX}\) 和 \(\overrightarrow {PQ}\) 的顺逆时针关系判断。特判:\(O,P,X\) 三点共线时,先要在排序时让 \(OP>OQ\),然后判断 \(OX\) 是否 \(\le OP\)。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll read(){
register char ch=getchar();register int x=0,f=1;
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return x*f;
}
const int N=1e5+5;
const double eps=1e-9;
int n,m,q,num;
struct P {int x,y,vx,vy;double tht;double d(){return sqrt(1.0*x*x+1.0*y*y);}}a[N],b[N],e[N*2],c[N*2],tmp[N];
P operator-(P a,P b){return P{a.x-b.x,a.y-b.y};}
ll operator*(P a,P b){return 1ll*a.x*b.y-1ll*a.y*b.x;}
void Hull(int &n,P a[N]){
sort(a+1,a+n+1,[](P a,P b){return a.x<b.x;});
static int stk[N],hul[N*2];
for(int i=1;i<=n;i++)tmp[i]=a[i];
int tp=0,cnt=0;
for(int i=1;i<=n;i++){
while(tp>1&&(a[stk[tp]]-a[stk[tp-1]])*(a[i]-a[stk[tp]])<=0)tp--;
stk[++tp]=i;
}
for(int i=1;i<=tp;i++)hul[++cnt]=stk[i];
tp=0;
for(int i=n;i;i--){
while(tp>1&&(a[stk[tp]]-a[stk[tp-1]])*(a[i]-a[stk[tp]])<=0)tp--;
stk[++tp]=i;
}
for(int i=1;i<=tp;i++)hul[++cnt]=stk[i];
sort(hul+1,hul+cnt+1),cnt=unique(hul+1,hul+cnt+1)-hul-1;
n=cnt;
for(int i=2;i<=cnt;i++)a[i]=tmp[hul[i]],a[i].tht=atan2(a[i].y-a[1].y,a[i].x-a[1].x);
sort(a+2,a+n+1,[](P a,P b){return a.tht<b.tht;});
}
bool fl;
bool inpoly(P a,int n,P b[N]){
if(a.tht<b[1].tht||a.tht>b[n].tht)return 0;
if(abs(a.tht-b[n].tht)<eps)return a.d()<=b[n].d();
int L=0,R=n+1,mid;
while(L<R-1){
mid=L+R>>1;
if(b[mid].tht>eps+a.tht)R=mid;
else L=mid;
}//cout<<b[R-1].x<<' '<<b[R-1].y<<'\n'<<b[R].x<<' '<<b[R].y<<'\n'<<a.x<<' '<<a.y<<'\n';
return (a-b[R])*(b[R-1]-b[R])>=0;
}
int main(){
n=read(),m=read(),q=read();
for(int i=1;i<=n;i++)a[i].x=read(),a[i].y=read();
for(int i=1;i<=m;i++)b[i].x=-read(),b[i].y=-read();
Hull(n,a),Hull(m,b);
a[0]=a[n],b[0]=b[m];
for(int i=1;i<=n;i++)
e[++num]=P{0,0,a[i].x-a[i-1].x,a[i].y-a[i-1].y,atan2(a[i].y-a[i-1].y,a[i].x-a[i-1].x)};
for(int i=1;i<=m;i++)
e[++num]=P{0,0,b[i].x-b[i-1].x,b[i].y-b[i-1].y,atan2(b[i].y-b[i-1].y,b[i].x-b[i-1].x)};
sort(e+1,e+num+1,[](P a,P b){return a.tht<b.tht;});
int mnax=2e9,mnbx=2e9,mnay=2e9,mnby=2e9,mncx=2e9,mncy=2e9;
for(int i=1;i<=n;i++)mnax=min(mnax,a[i].x),mnay=min(mnay,a[i].y);
for(int i=1;i<=m;i++)mnbx=min(mnbx,b[i].x),mnby=min(mnby,b[i].y);
P poi=P{0,0};
for(int i=1;i<=num;i++)c[i]=P{poi.x,poi.y,0,0,atan2(poi.y,poi.x)},poi.x+=e[i].vx,poi.y+=e[i].vy;
sort(c+1,c+num+1,[](P a,P b){return abs(a.tht-b.tht)<eps?a.d()<b.d():a.tht<b.tht;});
//for(int i=1;i<=n;i++)cout<<a[i].x<<' '<<a[i].y<<'\n';
for(int i=1;i<=num;i++)mncx=min(mncx,c[i].x),mncy=min(mncy,c[i].y);
//printf("%d %d %d %d %d %d\n",mnax,mnay,mnbx,mnby,mncx,mncy);
for(int i=1,x,y;i<=q;i++){
x=read()+mncx-mnax-mnbx,y=read()+mncy-mnay-mnby;
//if(i==62720)cerr<<x<<' '<<y<<'\n',fl=1;
//cout<<x<<' '<<y<<'\n';
cout<<inpoly(P{x,y,0,0,atan2(y,x)},num,c)<<'\n';//if(i==62720)fl=0;
}
}