1. 程式人生 > >【bzoj2732】[HNOI2012]射箭 二分+半平面交

【bzoj2732】[HNOI2012]射箭 二分+半平面交

mat esp 其余 兩個 using () 由於 define ring

題目描述

沫沫最近在玩一個二維的射箭遊戲,如下圖 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 。

輸出

僅包含一個整數,表示最多的通關數。

樣例輸入

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

樣例輸出

3


題解

二分+半平面交

題目一眼二分答案(別問我怎麽看出的。。做題的經驗告訴的我),然後轉化為判定性問題。

那麽如何判斷能否滿足條件?考慮每個限制條件:$\begin{cases}ax_i^2+bx_i\ge y_i\\ax_i^2+bx_i\le z_i\end{cases}$。

這裏面的ab是變量,那麽可以將限制條件看做是aOb平面直角坐標系上的幾何限制條件,不等式有解<=>限制的條件有公共部分。

所以將限制條件轉化為半平面求交即可。

真簡單= =

本體最惡心之處在於細節、細節、細節。。。只有細節。。。

細節1:a必須小於0,b必須大於0,因此需要將半平面交限制在第二象限,所以需要添加兩條輔助線。。。

細節2:判斷半平面是否有交,需要保證有交的時候交需要是一個封閉多邊形。因此需要再加兩條輔助線。。。
細節3:eps這個東西是真的煩人。。。本題的eps應設為1e-18,且在處理限制條件時加(因為等於的時候也算滿足條件)和限制象限時加(因為不能等於),其余時候不能加。並且需要使用long double。
細節4:本題卡常。。。所以所有小函數必須加inline不然會GG。。。排序是需要先把極角預處理出來,不能在比較時現求,否則也會GG。。。

直接上代碼吧:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 200010
#define eps 1e-18
using namespace std;
typedef long double ld;
struct point
{
    ld x , y;
    point() {}
    point(ld a , ld b) {x = a , y = b;}
    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*(const ld &a)const {return point(x * a , y * a);}
}p[N];
struct line
{
    point p , v;
    ld ang;
    line() {}
    line(ld a , ld b , ld c , ld d) {p = point(a , b) , v = point(c , d) , ang = atan2(v.y , v.x);}
}a[N] , q[N];
ld px[N] , py[N] , pz[N];
inline ld cross(point a , point b) {return a.x * b.y - a.y * b.x;}
inline bool left(line a , point b) {return cross(a.v , b - a.p) > 0;}
inline point inter(line a , line b)
{
    point u = a.p - b.p;
    ld tmp = cross(b.v , u) / cross(a.v , b.v);
    return a.p + a.v * tmp;
}
bool cmp(const line &a , const line &b)
{
    return a.ang == b.ang ? left(a , b.p) : a.ang < b.ang;
}
bool judge(int mid)
{
    int i , tot = 1 , l = 1 , r = 1;
    for(i = 1 ; i <= mid ; i ++ ) a[i] = line(0 , py[i] / px[i] - eps , -1 , px[i]) , a[i + mid] = line(0 , pz[i] / px[i] + eps , 1 , -px[i]);
    a[mid * 2 + 1] = line(-eps , eps , 0 , -1) , a[mid * 2 + 2] = line(-eps , eps , -1 , 0);
    a[mid * 2 + 3] = line(-1e10 , 1e10 , 0 , 1) , a[mid * 2 + 4] = line(-1e10 , 1e10 , 1 , 0);
    sort(a + 1 , a + mid * 2 + 5 , cmp);
    for(i = 2 ; i <= mid * 2 + 4 ; i ++ )
        if(a[i].ang != a[i - 1].ang)
            a[++tot] = a[i];
    q[1] = a[1];
    for(i = 2 ; i <= tot ; i ++ )
    {
        while(l < r && left(a[i] , p[r - 1])) r -- ;
        while(l < r && left(a[i] , p[l])) l ++ ;
        q[++r] = a[i];
        if(l < r) p[r - 1] = inter(q[r - 1] , q[r]);
    }
    while(l < r && left(q[l] , p[r - 1])) r -- ;
    p[r] = inter(q[r] , q[l]);
    return r - l > 1;
}
int main()
{
    int n , i , l = 1 , r , mid , ans = 0;
    scanf("%d" , &n) , r = n;
    for(i = 1 ; i <= n ; i ++ ) scanf("%Lf%Lf%Lf" , &px[i] , &py[i] , &pz[i]);
    while(l <= r)
    {
        mid = (l + r) >> 1;
        if(judge(mid)) ans = mid , l = mid + 1;
        else r = mid - 1;
    }
    printf("%d\n" , ans);
    return 0;
}

【bzoj2732】[HNOI2012]射箭 二分+半平面交