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
",min[i],max[i]);
}
return 0;
}