P4557-[JSOI2018]战争【凸包,闵可夫斯基和】

正题

题目连接:https://www.luogu.com.cn/problem/P4557


题目大意

给出两个点集\(A,B\)\(q\)次询问给出一个向量\(v\),询问将\(B\)中所有点加上向量\(v\)后两个集合的凸包是否有交。

\(1\leq n,m,q\leq 10^5\)


解题思路

闵可夫斯基和定义了两个向量集合的和,这里只讨论凸包的闵可夫斯基和。

\(A+B\),那么相当于以\(A\)凸包绕着\(B\)的凸包跑一圈形成的图形(位置不重要,这里的都是相对位置)。

然后求法就是实际上求和后的每条边是由原来的边组成的一个大凸包,所以我们直接把原来的两个凸包的边归并排序,然后再逐一算出每个点的位置就好了。

然后这个东西有什么用的,我也不知道。

但是看这道题,我们求出\(A+(-B)\),这样就大致\(A\)\(B\)的位置的形状,此时对于询问向量\(v\),如果它在\(A+(-B)\)内那么就是有交的,否则就是无交的。

时间复杂度:\(O(n\log n)\)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define ll long long
using namespace std;
const ll N=1e5+10;
struct vec{
	ll x,y;
	vec(ll xx=0,ll yy=0)
	{x=xx;y=yy;return;}
	ll len(){return x*x+y*y;}
}a[N],b[N],c[N],d[N],p[N<<1];
ll n,m,q,tot;
vector<vec> s;
vec operator+(const vec &a,const vec &b)
{return vec(a.x+b.x,a.y+b.y);}
vec operator-(const vec &a,const vec &b)
{return vec(a.x-b.x,a.y-b.y);}
ll operator^(const vec &a,const vec &b)
{return a.x*b.y-a.y*b.x;}
bool cmp(vec x,vec y)
{return (x.x==y.x)?(x.y<y.y):(x.x<y.x);}
bool cMp(vec x,vec y)
{return (x^y)>0||((x^y)==0&&x.len()<y.len());}
#define top (s.size()-1)
void Convex(vec *p,ll &n){
	while(!s.empty())s.pop_back();
	sort(p+1,p+1+n,cmp);vec bas=p[1];
	for(ll i=1;i<=n;i++)p[i]=p[i]-bas;
	sort(p+2,p+1+n,cMp);s.push_back(p[1]);
	for(ll i=2;i<=n;i++){
		while(top>0&&((s[top]-s[top-1])^(p[i]-s[top-1]))<=0)s.pop_back();
		s.push_back(p[i]);
	}
	for(ll i=0;i<=top;i++)p[i+1]=s[i]+bas;
	n=s.size();return;
}
#undef top
void Minkowski(){
	for(ll i=1;i<n;i++)c[i]=a[i+1]-a[i];c[n]=a[1]-a[n];
	for(ll i=1;i<m;i++)d[i]=b[i+1]-b[i];d[m]=b[1]-b[m];
	ll l=1,r=1;p[1]=a[1]+b[1];tot=1;
	while(l<=n&&r<=m)
		tot++,p[tot]=p[tot-1]+(((c[l]^d[r])>=0)?c[l++]:d[r++]);
	while(l<=n)tot++,p[tot]=p[tot-1]+c[l++];
	while(r<=m)tot++,p[tot]=p[tot-1]+d[r++];
	return;
}
bool Inside(vec x){
	if((x^p[1])>0||(x^p[tot])<0)return 0;
	ll pos=lower_bound(p+1,p+1+tot,x,cMp)-p-1;
	return ((x-p[pos])^(p[pos%tot+1]-p[pos]))<=0;
}
signed main()
{
	scanf("%lld%lld%lld",&n,&m,&q);
	for(ll i=1;i<=n;i++)scanf("%lld%lld",&a[i].x,&a[i].y);
	for(ll i=1;i<=m;i++)scanf("%lld%lld",&b[i].x,&b[i].y),b[i]=vec(0,0)-b[i];
	Convex(a,n);Convex(b,m);
	Minkowski();
	Convex(p,tot);
	vec bas=p[1];
	for(ll i=1;i<=tot;i++)p[i]=p[i]-bas;
	while(q--){
		vec x;
		scanf("%lld%lld",&x.x,&x.y);
		printf("%lld\n",Inside(x-bas));
	}
	return 0;
}
posted @ 2022-05-09 20:49  QuantAsk  阅读(36)  评论(0编辑  收藏  举报