1. 程式人生 > 其它 >P3122 [USACO15FEB]Fencing the Herd G

P3122 [USACO15FEB]Fencing the Herd G

https://www.luogu.com.cn/problem/P3122
cdq 分治+凸包

發現一個線是有用的等價於對於所有的 \((x_0,y_0)\) 都有 \(Ax_0+By_0<C\) 或都有 \(Ax_0+By_0>C\)
於是問題就轉化為了對於每個直線求 \(Ax_0+By_0\) 的最大和最小值

考慮把一開始的 \(n\) 個點也看成插入,問每條直線是否有用就是查詢上面說的最大、最小值,於是 cdq 分治,在 \([l,r]\) 區間裡把左右子區間中,各自的插入對自己中的查詢的影響遞迴處理,於是只用計算左區間中插入點對右區間中查詢的影響
不妨設 \(B>0\),先考慮如何找最大值,則有 \(Ax+By=\max \Rightarrow y=-\dfrac{A}{B}x+\dfrac{\max}{B}\)

,於是只用最大化截距即可
由於斜率固定,那麼就是一條直線從上往下平移,第一次碰到左區間中的一個點的時候停止,此時直線的截距乘上 \(B\) 即為最大值
於是可以把左區間中的點的上凸殼找出來,然後凸殼上的點 \(p_i\) 要更新的就是斜率在 \([\operatorname{slope}(p_i,p_{i+1}),\operatorname{slope}(p_{i-1},p_i)]\),拿兩個指標掃一遍就行
當然找最小值就也是同理了,在下凸殼上掃就行

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define reg register
#define INT_INF (int)(0x3f3f3f3f)
#define LL_INF (long long)(0x3f3f3f3f3f3f3f3f)
#define EN puts("")
inline long long read(){
	register long long x=0;register int y=1;register char c=getchar();
	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
	return y?x:-x;
}
#define N 200006
#define double long long
struct Vector{
	double x,y;
	inline int operator < (const Vector &b)const{return x==b.x?y<b.y:x<b.x;}
	inline Vector operator - (const Vector &b)const{return (Vector){x-b.x,y-b.y};}
	inline double operator * (const Vector &b)const{return x*b.x+y*b.y;}
	inline double operator ^ (const Vector &b)const{return x*b.y-y*b.x;}
};
struct Node{
	Vector way;int id;
	inline int operator < (const Node &b)const{return (way^b.way)<0;}
};
struct Query{
	int t;
	long long a,b,c;
}Q[N];
long long max[N],min[N];
inline void update(reg int id,const Vector &p){
	long long o=p.x*Q[id].a+p.y*Q[id].b;
	max[id]=std::max(max[id],o);min[id]=std::min(min[id],o);
}
Vector p[N],q[N];
Node line[N];
void work(reg int l,reg int r){
	if(l>=r) return;
	int mid=(l+r)>>1;
	work(l,mid);work(mid+1,r);
	int totL=0,totR=0;
	for(reg int i=l;i<=mid;i++)if(Q[i].t==1) p[++totL]=(Vector){Q[i].a,Q[i].b};
	for(reg int i=mid+1;i<=r;i++)if(Q[i].t==2) line[++totR]=(Node){(Vector){Q[i].b,-Q[i].a},i};
	if(!totL||!totR) return;
	std::sort(line+1,line+1+totR);
	std::sort(p+1,p+1+totL);
	int top=1;q[1]=p[1];
	for(reg int i=2;i<=totL;i++){
		while(top>1&&((q[top]-q[top-1])^(p[i]-q[top]))>=0) top--;
		q[++top]=p[i];
	}
	for(reg int i=1,pos=1;i<=top;i++){
		while(pos<=totR&&(i==top||(line[pos].way^(q[i+1]-q[i]))<=0)) update(line[pos++].id,q[i]);
	}
	for(reg int i=1,j=totR;i<j;i++,j--) std::swap(line[i],line[j]);
	top=1;q[1]=p[1];
	for(reg int i=2;i<=totL;i++){
		while(top>1&&((q[top]-q[top-1])^(p[i]-q[top]))<=0) top--;
		q[++top]=p[i];
	}
	for(reg int i=1,pos=1;i<=top;i++){
		while(pos<=totR&&(i==top||(line[pos].way^(q[i+1]-q[i]))>=0)) update(line[pos++].id,q[i]);
	}
}
int main(){
//		std::freopen("15.in","r",stdin);
	int n=read(),q=read()+n;
	for(reg int i=1;i<=n;i++) Q[i].t=1,Q[i].a=read(),Q[i].b=read();
	for(reg int i=n+1;i<=q;i++){
		Q[i].t=read();
		if(Q[i].t==1) Q[i].a=read(),Q[i].b=read();
		else{
			Q[i].a=read();Q[i].b=read();Q[i].c=read();
			if(Q[i].b<0) Q[i].a=-Q[i].a,Q[i].b=-Q[i].b,Q[i].c=-Q[i].c;
			max[i]=-2e18;min[i]=2e18;
		}
	}
	work(1,q);
	for(reg int i=n+1;i<=q;i++)if(Q[i].t==2){
		puts((max[i]<Q[i].c||min[i]>Q[i].c)?"YES":"NO");
//			printf("%lld %lld\n",min[i],max[i]);
	}
	return 0;
}