闵可夫斯基和简介
两个凸包 ,分别有 个端点,记 表示点 在多边形内(含边界), 和 的闵可夫斯基和定义为( 表示向量相加):
使用反证法和肉眼观察尝试法不难得出:
- 是凸包
- 的边(共 条)是由原 的边一一平移得来
故:对 极角排序,得到它们的边集,并起来,按极角排序,按时针方向依次就是 的边集。
形状知道了,位置在哪里?
我的做法是求出 ,那么不难发现 。
练习:[JSOI2018]战争
所以要干两件事:
- 求 和 的闵可夫斯基和
- 快速判断点是否在多边形内
第一件事我们已经知道了。
第二件事,我们可以把凸包 的任意一个端点当做原点 , 在以 O 为原点、按照 atan2(逆时针)极角排序后的点集 中的后继 ,以及 的前驱 ,利用 和 的顺逆时针关系判断。特判: 三点共线时,先要在排序时让 ,然后判断 是否 。
复制#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; } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话