闵可夫斯基和简介

两个凸包 A,B,分别有 n,m 个端点,记 aA 表示点 a 在多边形内(含边界),AB 的闵可夫斯基和定义为(a+b 表示向量相加):

C={a+b|aA,bB}

使用反证法和肉眼观察尝试法不难得出:

  • 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]战争
b,b+xAxAB
所以要干两件事:

  1. AB 的闵可夫斯基和 C
  2. 快速判断点是否在多边形内

第一件事我们已经知道了。
第二件事,我们可以把凸包 C 的任意一个端点当做原点 OOX 在以 O 为原点、按照 atan2(逆时针)极角排序后的点集 C 中的后继 P,以及 P 的前驱 Q,利用 PXPQ 的顺逆时针关系判断。特判:O,P,X 三点共线时,先要在排序时让 OP>OQ,然后判断 OX 是否 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;
}
}
posted @   pengyule  阅读(472)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示