P3122 [USACO15FEB]Fencing the Herd G
阿新 • • 發佈:2021-06-23
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; }