BZOJ4617 : [Wf2016]Spin Doctor
将所有点投射到二维平面上,枚举形成答案的两个$1$类点作为端点,以及过这两点的两条平行直线,那么答案就是直线边上以及内部的点数。
显然只需要枚举$1$类点形成的凸包上的点。
如果只有一个$1$类点,那么答案是$1$。
如果凸包大小为$1$,那么只有和它重合的$0$类点会被计入答案。
如果凸包大小为$2$,那么只有线段上的所有点会被计入答案。
否则凸包大小至少为$3$,对于每个$0$类点,首先在凸包上按极角二分出一条边,看看是否在凸包内或者边上。
如果不在,那么往左往右二分出两条切线的位置,当平行线的斜率介于这之间时会夹住这个点。
求出所有关键事件后扫描线即可。
时间复杂度$O(n\log n)$。
#include<cstdio> #include<algorithm> using namespace std; typedef long long ll; const int N=250010; int n,m,ce,i,j,x,y,z,ca,cb,cnt,ret,mv; struct P{ int x,y; P(){} P(int _x,int _y){x=_x,y=_y;} P operator-(const P&b){return P(x-b.x,y-b.y);} void operator-=(const P&b){*this=*this-b;} bool operator==(const P&b){return x==b.x&&y==b.y;} bool operator!=(const P&b){return x!=b.x||y!=b.y;} }a[N],b[N],c[N<<1]; struct E{ P o;int t; E(){} E(P _o,int _t){ o=_o,t=_t; if(o.x<0)o.x*=-1,o.y*=-1; } }e[N<<1]; inline bool cmp(const P&a,const P&b){return a.x==b.x?a.y<b.y:a.x<b.x;} inline ll cross(const P&a,const P&b){return 1LL*a.x*b.y-1LL*a.y*b.x;} inline bool cmpe(const E&a,const E&b){return cross(a.o,b.o)<0;} inline void read(int&a){char c;while(!(((c=getchar())>='0')&&(c<='9')));a=c-'0';while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';} int convexhull(P*p,int n,P*q){ int i,k,m; for(i=m=0;i<n;q[m++]=p[i++])while(m>1&&cross(q[m-1]-q[m-2],p[i]-q[m-2])<=0)m--; k=m; for(i=n-2;i>=0;q[m++]=p[i--])while(m>k&&cross(q[m-1]-q[m-2],p[i]-q[m-2])<=0)m--; return --m; } inline bool point_on_segment(P p,P a,P b){ return !cross(b-a,p-a)&&1LL*(p.x-a.x)*(p.x-b.x)+1LL*(p.y-a.y)*(p.y-b.y)<=0; } inline int askl(int l,int r,P p){ int t=l++,mid; while(l<=r){ mid=(l+r)>>1; if(cross(c[mid]-p,c[(mid-1+n)%n]-c[mid])<=0)l=(t=mid)+1;else r=mid-1; } return t; } inline int askr(int l,int r,P p){ int t=r--,mid; while(l<=r){ mid=(l+r)>>1; if(cross(c[mid]-p,c[(mid+1)%n]-c[mid])>=0)r=(t=mid)-1;else l=mid+1; } return t; } inline void solve(P p){ if(point_on_segment(p,c[0],c[n-1])){cnt++;return;} int o=0; if(p.x>0){ int l=1,r=n-1,mid; while(l<=r)if(cross(c[mid=(l+r)>>1],p)>=0)l=(o=mid)+1;else r=mid-1; }else if(p.y>0)o=n-1; if(p.x>=0&&cross(p-c[o],c[o+1]-p)<0){cnt++;return;} if(p.x>=0&&point_on_segment(p,c[o],c[o+1])){cnt++;return;} int l,r; if(p.x>0)l=askl(0,o,p),r=askr(o,n,p);else l=askl(m,n,p),r=askr(0,m,p); e[++ce]=E(p-c[l],1); e[++ce]=E(p-c[r],-1); if(cmpe(e[ce],e[ce-1]))cnt++; } int main(){ read(n); while(n--){ read(x),read(y),read(z); if(z)a[++ca]=P(x,y);else b[++cb]=P(x,y); } if(ca==1)return puts("1"),0; cnt=ca; sort(a+1,a+ca+1,cmp); for(ca=0,i=1;i<=cnt;i++)if(i==1||a[i]!=a[i-1])a[++ca]=a[i]; if(ca==1){ for(i=1;i<=cb;i++)if(b[i]==a[1])cnt++; return printf("%d",cnt),0; } if(ca==2){ for(i=1;i<=cb;i++)if(point_on_segment(b[i],a[1],a[2]))cnt++; return printf("%d",cnt),0; } n=convexhull(a+1,ca,c); for(i=1;i<n;i++)c[i]-=c[0]; for(i=1;i<=cb;i++)b[i]-=c[0]; c[0]-=c[0]; for(i=0;i<n;i++)if(c[i].x>=c[m].x)m=i; for(i=0;i<n;i++)c[i+n]=c[i]; for(i=1;i<=cb;i++)solve(b[i]); sort(e+1,e+ce+1,cmpe); for(i=1;i<=ce;i=j){ for(j=i;j<=ce&&!cross(e[i].o,e[j].o);j++)ret+=e[j].t; if(ret<mv)mv=ret; } return printf("%d",cnt+mv),0; }