ABC251G

发布时间 2023-12-23 21:45:46作者: yinhee

提供一个本质相同,但是不需要会向量也能做,而且很好想的方法。

首先发现凸包点少,也就意味着边少,考虑从边的方向寻找突破口。

考虑一个凸包的本质:若干个直线划分出若干个半平面,它们的交即为这个凸包。如果一个点对于每一条直线,都在于凸包的同侧,那么这个点就在这个凸包内。

这样直接暴力做仍然是 \(O(nmq)\) 的。但是明显发现每条直线相当于限制了,经过一个点 \((x,y)\),斜率为 \(k\) 的直线的截距不小于/大于某个数。取其中最大/小的一个限制即可。\(O(nm)\) 预处理出来,\(O(nq)\) 解决。

要注意的是,这种写法细节比较多。平行于坐标轴的情况建议全部特判,test 5 极度卡精度,可以手写分数类解决。

比向量解法相对复杂,但是也很好写。

code:

点击查看代码
int n,m,q,a[N],b[N],op[N];
const double eps=1e-8;
struct frac{
	ll a,b;
	frac(ll _a=0,ll _b=1){
		a=_a,b=_b;
		if(b<0)a=-a,b=-b;
	}
	il bool operator<=(const frac &tmp)const{
		return (__int128)a*tmp.b<=(__int128)tmp.a*b;
	}
	il bool operator<(const frac &tmp)const{
		return (__int128)a*tmp.b<(__int128)tmp.a*b;
	}
	il frac operator-(const frac &tmp)const{
		return frac(a*tmp.b-b*tmp.a,b*tmp.b);
	}
	il frac operator*(const frac &tmp)const{
		return frac(a*tmp.a,b*tmp.b);
	}
}f[N],sl[N];
void check(int i,int j){
	if(a[i]==a[j]){
		if(b[i]<b[j])op[i]=2;
		else op[i]=4;
	}else if(b[i]==b[j]){
		if(a[i]>a[j])op[i]=3;
		else op[i]=5;
	}else{
		sl[i]=frac(b[i]-b[j],a[i]-a[j]);
		if(a[i]<a[j])op[i]=0;
		else op[i]=1;
	}
}
void Yorushika(){
	scanf("%d",&n);
	rep(i,1,n){
		scanf("%d%d",&a[i],&b[i]);
		if(i==1)
			continue;
		check(i,i-1);
	}
	check(1,n);
	rep(i,1,n){
		if(op[i]>=1&&op[i]<=3)f[i]=frac(-1ll*inf*inf,1);
		else f[i]=frac(1ll*inf*inf,1);
	}
	scanf("%d",&m);
	rep(j,1,m){
		int x,y;
		scanf("%d%d",&x,&y);
		rep(i,1,n){
			frac nx=frac(x+a[i],1),ny=frac(y+b[i],1);
			if(op[i]==0)f[i]=min(f[i],ny-nx*sl[i]);
			else if(op[i]==1)f[i]=max(f[i],ny-nx*sl[i]);
			else if(op[i]==2)f[i]=max(f[i],nx);
			else if(op[i]==3)f[i]=max(f[i],ny);
			else if(op[i]==4)f[i]=min(f[i],nx);
			else f[i]=min(f[i],ny);
		}
	}
	scanf("%d",&q);
	rep(j,1,q){
		frac x,y;
		scanf("%lld%lld",&x.a,&y.a);
		bool flag=true;
		rep(i,1,n){
			if(op[i]==0)flag&=y-x*sl[i]<=f[i];
			else if(op[i]==1)flag&=f[i]<=y-x*sl[i];
			else if(op[i]==2)flag&=f[i]<=x;
			else if(op[i]==3)flag&=f[i]<=y;
			else if(op[i]==4)flag&=x<=f[i];
			else flag&=y<=f[i];
		}
		if(flag)
			puts("Yes");
		else 
			puts("No");
	}
}
signed main(){
	int t=1;
	//	scanf("%d",&t);
	while(t--)
		Yorushika();
}

题外话:

zlt 怎么又做过?zlt 怎么又做过?zlt 怎么又做过?