[HNOI 2012] 射箭 archery

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

对于每一关 (x, y1, y2), 有 ax2 + bx >= y1, ax2 + bx <= y2, 再把 a 当作 x,b 当作 y,原式变为 kx + ly >= q, 就可以发现就是线性规划。

很容易秒出 O(nlg2n) 的算法,就是对前面每一关做一次半平面交。然后就马上可以想到二分答案了。

对于小于等于号的处理,我对于大的一方加上了一个 eps 。

然后就是这道题卡精度。。。。加了 5 个 0 就 A 了。。。

Code :

#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 200007
#define R(a) const a &
#define oo 1000000000000000000.
#define eps 0.00000000000000001
  
struct point{double x, y;};
struct line{point f; double c;};
  
double operator *(R(point) a) {return sqrt(a.x * a.x + a.y * a.y);}
double operator *(R(point) a, R(point) b) {return a.x * b.y - a.y * b.x;}
double operator /(R(point) a, R(point) b) {return a.x * b.x + a.y * b.y;}
point operator *(R(line) a, R(line) b)
{
  R(double) c = a.f * b.f;
  return (point) {(b.f.y * a.c - a.f.y * b.c) / c, (a.f.x * b.c - b.f.x * a.c) / c};
}
  
int n, m, * h, * t, l, r, mid;
int p[maxn], q[maxn], map[maxn], pp[maxn];
double x, y, z;
double d[maxn], e[maxn];
line a[maxn];
  
int lfcmp(R(double) a, R(double) b)
{
  R(double) c = a - b;
  if (c < - eps) return - 1;
  else return c > eps;
}
  
bool cmp(R(int) i, R(int) j)
{
  return d[i] < d[j] || (! lfcmp(d[i], d[j]) && e[i] > e[j]);
}
  
void getline(R(double) x, R(double) y, R(double) z)
{
  a[++ m] = (line) {(point) {x, y}, z};
  p[m] = m, d[m] = atan2(y, x), e[m] = z / * a[m].f;
}
  
bool okay(int n)
{
  int tot = 0; h = q + 1, t = q;
  for (int i = 1; i <= m; ++ i) if (map[p[i]] <= n) pp[++ tot] = p[i];
  for (int i = 1; i <= tot; ++ i) if (i == 1 || lfcmp(d[pp[i]], d[pp[i - 1]]))
  {
    while (t - h > 0 && a[* t] * a[* (t - 1)] / a[pp[i]].f < a[pp[i]].c + eps) -- t;
    while (t - h > 0 && a[* h] * a[* (h + 1)] / a[pp[i]].f < a[pp[i]].c + eps) ++ h;
    * (++ t) = pp[i];
  }
  while (t - h > 1 && a[* t] * a[* (t - 1)] / a[* h].f < a[* h].c + eps) -- t;
  while (t - h > 1 && a[* h] * a[* (h + 1)] / a[* t].f < a[* t].c + eps) ++ h;
  return t - h > 1;
}
  
int main()
{
  scanf("%d", & n);
  for (int i = 1; i <= n; ++ i)
  {
    scanf("%lf%lf%lf", & x, & y, & z);
    getline(x * x, x, y - eps), map[m] = i;
    getline(- x * x, - x, - z - eps), map[m] = i;
  }
  getline(1, 0, - oo), getline(- 1, 0, 0);
  getline(0, - 1, - oo), getline(0, 1, 0);
  sort(p + 1, p + m + 1, cmp), l = 1, r = n;
  if (okay(r)) printf("%d", r), exit(0);
  for (mid = (l + r) >> 1; l < r; mid = (l + r) >> 1)
    if (okay(mid)) l = mid + 1; else r = mid;
  printf("%d", r - 1);
    
  return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值