[HNOI2012]射箭

题目描述

沫沫最近在玩一个二维的射箭游戏,如下图 1 所示,这个游戏中的 x 轴在地面,第一象限中有一些竖直线段作为靶子,任意两个靶子都没有公共部分,也不会接触坐标轴。

沫沫控制一个位于(0,0)的弓箭手,可以朝 0 至 90?中的任意角度(不包括 0度和 90度),以任意大小的力量射出带有穿透能力的光之箭。由于游戏中没有空气阻力,并且光之箭没有箭身,箭的轨迹会是一条标准的抛物线,被轨迹穿过的所有靶子都认为被沫沫射中了,包括那些 只有端点被射中的靶子。

这个游戏有多种模式,其中沫沫最喜欢的是闯关模式。

在闯关模式中,第一关只有一个靶 子,射中这个靶子即可进入第二关,这时在第一关的基础上会出现另外一个靶子,若能够一箭 双雕射中这两个靶子便可进入第三关,这时会出现第三个靶子。依此类推,每过一关都会新出 现一个靶子,在第 K 关必须一箭射中前 K 关出现的所有 K 个靶子才能进入第 K+1 关,否则游戏 结束。

沫沫花了很多时间在这个游戏上,却最多只能玩到第七关”七星连珠“,这让她非常困惑。 于是她设法获得了每一关出现的靶子的位置,想让你告诉她,最多能通过多少关

输入输出格式

输入格式:

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

输出格式:

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

输入输出样例

输入样例#1:

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

输出样例#1:

3


题解

对于每个限制\((x1,y1),(x2,y2)\)需要满足
\(ax1^2+bx1>=y1\\ax2^2+bx2<=y2\)
然后可以看做是\(ax1+b>=\frac{y1}{x1}\\ax2+b<=\frac{y2}{x2}\)
移一下项\(b>=\frac{y1}{x1}-ax1\\b<=\frac{y2}{x2}-ax2\)
然后就可以把每个限制想成两条直线,把\(b\)作为坐标轴
做半平面交即可

代码

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
# define double long double
const int M = 300050 ;
const double INF = 1e18 ;
const double EPS = 1e-19 ;
using namespace std ;

inline int read() {
    char c = getchar() ; int x = 0 , w = 1 ;
    while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
    while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
    return x*w ;
}

int n , cnt ;
inline int sgn(double x) {
    if(fabs(x) < EPS) return 0 ;
    return x > 0 ? 1 : -1 ;
}
struct Vec {
    double x , y ;
    Vec (double Tx = 0 , double Ty = 0) {
        x = Tx , y = Ty ;
    }
} p[M] ;

inline Vec operator + (Vec A , Vec B) {
    return (Vec) { A.x + B.x , A.y + B.y } ;
}
inline Vec operator - (Vec A , Vec B) {
    return (Vec) { A.x - B.x , A.y - B.y } ;
}
inline Vec operator * (Vec A , double B) {
    return (Vec) { A.x * B , A.y * B } ;
}
inline Vec operator / (Vec A , double B) {
    return (Vec) { A.x / B , A.y / B } ;
}

struct Line {
    int id ;
    Vec s , t , v ; double Ang ;
    Line () { } ;
    Line (int idt , Vec A , Vec B) {
        id = idt ; s = A ; t = B ; 
        v = t - s ; Ang = atan2(v.y , v.x) ;
    }
} l[M] , que[M] ;

inline double Dot(Vec A , Vec B) {
    return A.x * B.x + A.y * B.y ;
}
inline double Cross(Vec A , Vec B) {
    return A.x * B.y - A.y * B.x ;
}
inline bool operator < (Line A , Line B) {
    if(sgn(A.Ang - B.Ang) != 0) return A.Ang < B.Ang ;
    else return sgn(Cross(A.v , B.s - A.s)) < 0 ; 
}
inline bool Is_para(Line A , Line B) {
    return sgn(Cross(A.v , B.v)) == 0 ;
}
inline bool OnRight(Vec P , Line L) {
    return sgn(Cross(P - L.s , L.v)) > 0 ;
}
inline Vec GLI(Line A , Line B) {
    Vec c = B.s - A.s ;
    double t = Cross(B.v , c) / Cross(B.v , A.v) ;
    return A.s + A.v * t ;
}

inline bool check(int mid) {
    int i = 1 , head = 1 , tail = 1 ;
    while(l[i].id > mid) 
        ++ i ;
    que[tail] = l[i] ;
    for(++ i ; i <= cnt ; i ++) {
        if(l[i].id > mid) continue ;
        if(head < tail && (Is_para(que[head] , que[head + 1]) || Is_para(que[tail - 1] , que[tail])))
            return false ;
        while(head < tail && OnRight(p[tail - 1] , l[i])) -- tail ;
        while(head < tail && OnRight(p[head] , l[i])) ++ head ;
        if(sgn(que[tail].Ang - l[i].Ang)) que[++tail] = l[i] ;
        else continue ;
        if(head < tail) p[tail - 1] = GLI(que[tail] , que[tail - 1]) ;
    }
    while(head < tail && OnRight(p[tail - 1] , que[head])) -- tail ;
    while(head < tail && OnRight(p[head] , que[tail])) ++ head ;
    if(tail - head <= 1) return false ;
    return true ;
}
int main() {
    n = read() ;
    for(int i = 1 , x , yf , ys ; i <= n ; i ++) {
        x = read() ; yf = read() ; ys = read() ;
        Vec p1 = Vec (0.0 , (double)yf / x) ; Vec p2 = Vec (1.0 , (double)yf / x - x) ;
        Vec p3 = Vec (1.0 , (double)ys / x - x) ; Vec p4 = Vec (0.0 , (double)ys / x) ;
        l[++cnt] = Line ( i , p1 , p2 ) ; l[++cnt] = Line ( i , p3 , p4 ) ;
    }
    l[++cnt] = Line ( 0 , Vec(-INF , EPS) , Vec(-EPS , EPS) ) ;
    l[++cnt] = Line ( 0 , Vec(-EPS , EPS) , Vec(-EPS , INF) ) ;
    l[++cnt] = Line ( 0 , Vec(-EPS , INF) , Vec(-INF , INF) ) ;
    l[++cnt] = Line ( 0 , Vec(-INF , INF) , Vec(-INF , EPS) ) ;
    sort(l + 1 , l + cnt + 1) ;
    int l = 1 , r = n , ret = 0 ;
    while(l <= r) {
        int mid = (l + r) >> 1 ;
        if(check(mid)) l = mid + 1 , ret = mid ;
        else r = mid - 1 ;
    }
    printf("%d\n",ret) ;
    return 0 ;
}
posted @ 2019-03-11 21:09  beretty  阅读(171)  评论(0编辑  收藏  举报