BZOJ 2732 二分+半平面相交 解题报告

来源:互联网 发布:力学软件 编辑:程序博客网 时间:2024/06/08 02:18

2732: [HNOI2012]射箭

Description

沫沫最近在玩一个二维的射箭游戏,如下图 1 所示,这个游戏中的 x 轴在地面,第一象限中有一些竖直线段作为靶子,任意两个靶子都没有公共部分,也不会接触坐标轴。沫沫控制一个位于(0,0)的弓箭手,可以朝 0 至 90?中的任意角度(不包括 0度和 90度),以任意大小的力量射出带有穿透能力的光之箭。由于游戏中没有空气阻力,并且光之箭没有箭身,箭的轨迹会是一条标准的抛物线,被轨迹穿过的所有靶子都认为被沫沫射中了,包括那些 只有端点被射中的靶子。这个游戏有多种模式,其中沫沫最喜欢的是闯关模式。在闯关模式中,第一关只有一个靶 子,射中这个靶子即可进入第二关,这时在第一关的基础上会出现另外一个靶子,若能够一箭 双雕射中这两个靶子便可进入第三关,这时会出现第三个靶子。依此类推,每过一关都会新出 现一个靶子,在第 K 关必须一箭射中前 K 关出现的所有 K 个靶子才能进入第 K+1 关,否则游戏 结束。沫沫花了很多时间在这个游戏上,却最多只能玩到第七关“七星连珠”,这让她非常困惑。 于是她设法获得了每一关出现的靶子的位置,想让你告诉她,最多能通过多少关

Input

输入文件第一行是一个正整数N,表示一共有N关。接下来有N行,第i+1行是用空格隔开的三个正整数xi,yi1,yi2(yi1 < yi2 ),表示第i关出现的靶子的横坐标是xi,纵坐标的范围是从yi1到yi2 。
输入保证30%的数据满足N≤100,50%的数据满足N≤5000,100%的数据满足N≤100000且给 出的所有坐标不超过109 。

Output

仅包含一个整数,表示最多的通关数。

Sample Input

5
2 8 12
5 4 5
3 8 10
6 2 3
1 3 7

Sample Output

3
【解题报告】
设这个经过原点的抛物线为y = a * x ^ 2 + b * x,设一条线段的起点和终点为(x0,y1)和(x0,y2),且y2 > y1。
将x0带入到设出的抛物线中,会得到y = a * x0 ^ 2 + b * x0,这时候需要满足的是y <= y2 && y >= y1,也就是a * x0 ^ 2 + b * x0 <= y2 && y1 <= a * x0 ^ 2 + b * x0
整理一下思路,x0,y1,y2是已知量,a和b是我们设出来的量,不妨换一种写法,令x = a,y = b,k = x0,那么原不等式组就是
=> x * k ^ 2 + y * k <= y2 && x * k ^ 2 + y * k >= y1
=> x * k ^ 2 + y * k - y2 <= 0 && x * k ^2 + y * k - y1 >= 0
这样就很明显了,不等式组化成了两个半平面,之后利用半平面交判定是否存在就可以了。最外层套一个二分,时间复杂度大概是O(nlog^2n)

然而

这道题玄学卡精度!!!
我花了40分钟各种调,最后把eps改成1e-50才过。。。
给出题人寄刀片!!!

这里写图片描述

代码如下:

#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>using namespace std;#define N 200010struct Point{      long double x,y;             Point(long double _ = .0,long double __ = .0):x(_),y(__) {}      Point operator +(const Point &a)const     {          return Point(x+a.x,y+a.y);      }      Point operator -(const Point &a)const     {          return Point(x-a.x,y-a.y);      }      Point operator *(double a)const     {          return Point(x*a,y*a);      }  }p[N];struct Line{    Point p,v;    long double alpha;    Line(Point _,Point __):p(_),v(__)     {          alpha=atan2(v.y,v.x);      }Line(){}      bool operator <(const Line &a)const     {          return alpha<a.alpha;      }  }src[N],line[N],q[N];int t,lines;inline long double Cross(Point p1,Point p2)  {      return p1.x*p2.y-p1.y*p2.x;  }  inline bool OnLeft(Point p,Line l)  {      return Cross(l.v,p-l.p)>=0;//left or right}  inline Point GetIntersection(Line l1,Line l2)  {      Point u=l1.p-l2.p;      long double temp=Cross(l2.v,u)/Cross(l1.v,l2.v);      return l1.p+l1.v*temp;  }  #define EPS 1e-50#define DCMP(a) (fabs(a)<EPS)  inline bool HalfplaneIntersection(int lines)  {      int front=1,tail=1;      q[1]=line[1];      for(int i=2;i<=lines;++i)     {          while(front<tail&&!OnLeft(p[tail-1],line[i])) --tail;          while(front<tail&&!OnLeft(p[front],line[i])) ++front;          if(DCMP(Cross(q[tail].v,line[i].v)))              q[tail]=OnLeft(q[tail].p,line[i])?q[tail]:line[i];          else q[++tail]=line[i];          if(front<tail) p[tail-1]=GetIntersection(q[tail],q[tail-1]);      }      while(front<tail&&!OnLeft(p[tail-1],q[front])) --tail;      return tail-front>1;  }  inline bool Judge(int mid){    mid<<=1;      memcpy(line+1,src+1,sizeof(Line)*mid);      sort(line+1,line+mid+1);      return HalfplaneIntersection(mid);  }int main(){    scanf("%d",&t);    for(int i=1;i<=t;++i)    {        static double x,y1,y2;        scanf("%lf%lf%lf",&x,&y1,&y2);        src[++lines]=Line(Point(0,y2/x),Point(-1/x,1));          src[++lines]=Line(Point(0,y1/x),Point(1/x,-1));      }    int l=1,r=t,ans=1;    while(l<=r)    {        int mid=(l + r)>>1;          if(Judge(mid))              ans=mid,l=mid+1;          else r=mid-1;      }    printf("%d\n",ans);    return 0;}

让我看到你们的双手

阅读全文
0 1
原创粉丝点击