suxxsfe

一言(ヒトコト)

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;
}
posted @ 2021-06-23 21:06  suxxsfe  阅读(96)  评论(0编辑  收藏  举报