[JSOI2018] 战争

一、题目

点此看题

二、解法

哈哈哈,这道题我都给草过去了,计算几何学懂啦\(\sim\)

发现部落的管辖范围就是求凸包,那么我们先把两个部落的凸包求出来。

任意取第一个凸包的一点 \(a\),第二个凸包的一点 \(b\),设位移向量是 \(d\),那么两个凸包管辖范围不交等价于向量 \(v=a-b-d\) 非零,发现 \(a-b\) 是一种闵可夫斯基和的形式,我们把第二个凸包的所有点取反,然后做闵可夫斯基和,问题变成了判断点 \(d\) 在不在合并后的凸包中。

可以 \(O(\log n)\) 快速判断,首先把凸包左下角的点平移到 \((0,0)\),凸包的所有边本身就是有极角顺序的,设 \(c_i\) 为凸包上第 \(i\) 个点,翻译成叉积就是 \(c_i\times c_{i+1}<0\),也就是第 \(i\) 个点到第 \(i+1\) 个点有一个顺时针转角。

首先判断边界情况,然后找到待判定顺时针方向上的第一个凸包上的点,做一下叉积就可以判断该点是否在凸包上了。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define int long long
const int M = 200005;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,k,q;
struct point
{
	int x,y;
	point(int X=0,int Y=0) : x(X) , y(Y) {}
	point operator + (point b) {return point(x+b.x,y+b.y);}
	point operator - (point b) {return point(x-b.x,y-b.y);}
	int operator * (point b) {return x*b.y-y*b.x;}
	bool operator < (const point &b) const {
		return x==b.x?y<b.y:x<b.x;
	}
}a[M],b[M],c[M],v[M],s[M],t[M];
void conv(point *a,int &n)
{
	int m=0;
	sort(a+1,a+1+n);
	for(int i=1;i<=n;i++)
	{
		while(m>1 && (a[i]-v[m-1])*(v[m]-v[m-1])<=0) m--;
		v[++m]=a[i];
	}
	int tmp=m;
	for(int i=n-1;i>=1;i--)
	{
		while(m>tmp && (a[i]-v[m-1])*(v[m]-v[m-1])<=0) m--;
		v[++m]=a[i];
	}
	n=m-1;memcpy(a,v,sizeof v);
}
void merge()
{
	for(int i=1;i<=n;i++) s[i]=a[i%n+1]-a[i];
	for(int i=1;i<=m;i++) t[i]=b[i%m+1]-b[i];
	int i=1,j=1;c[++k]=a[1]+b[1];
	while(i<=n && j<=m) k++,c[k]=c[k-1]+(s[i]*t[j]<=0?s[i++]:t[j++]);
	while(i<=n) k++,c[k]=c[k-1]+s[i++];
	while(j<=m) k++,c[k]=c[k-1]+t[j++];
}
int cmp(point x,point y)
{
	return x*y<0;
}
int ask(point x)
{
	if(c[k]*x<0 || x*c[2]<0) return 0;
	int p=lower_bound(c+1,c+1+k,x,cmp)-c-1;
	return (x-c[p])*(c[p%k+1]-c[p])>=0;
}
signed 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();
	conv(a,n);conv(b,m);
	merge();
	conv(c,k);point bs=c[1];
	for(int i=1;i<=k;i++) c[i]=c[i]-bs;
	while(q--)
	{
		int x=read(),y=read();
		printf("%d\n",ask(point(x,y)-bs));
	}
}
上一篇:SSH Secure Shell Client 登录服务器出现“server responded algorithm negotiation failed”解决方法会报错的问题


下一篇:【题解 LOJ2546「JSOI2018」潜入行动】