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;
}

  

posted @ 2017-03-27 01:36  Claris  阅读(291)  评论(0编辑  收藏  举报