2020杭電多校第一場 hdu6759 Leading Robots


題目鏈接

http://acm.hdu.edu.cn/showproblem.php?pid=6759

題目大意

有 N 個機器人賽跑 , 第 i 個機器人初始速度為0 , 加速度為ai , 位置為 bi

現在所有機器人將開始行動 , 問有多少個機器人可以任意時刻當上第一

解題思路 

以 0 點為初始點, 那么第 i 個機器人行動了 t 秒后的位置為 $S_{i}=\dfrac{1}{2}a_{i}t^{2}+b_{i}$

我們令 y = Si , x = 1/2t^2 , ki = ai , di = bi

那么第 i 個機器人的運行軌跡就可以轉換為一元一次方程 yi = ki * x + di

如下圖

那么什么樣的運動軌跡可以在某個時刻成為第一名的呢

不難發現 , 我們以二維的視角從上往下看 , 能看到的直線對應的機器人則有機會成為第一

再仔細觀察 , 又可發現↓

對於一條直線C, 若存在一條斜率比它大的直線A,和斜率比它小的直線B

且 AC 的交點在BC的橫坐標上的左邊(對應下圖中)藍點和綠點 , 則直線C就會被 “覆蓋”

所以我們可以按照斜率從大到小排序 , 用單調棧維護

先將斜率最大的A入棧  , 然后將次大的C入棧 , 然后判斷當前直線 AC 的交點是否會在 BC的左邊

在則彈出 C 進行下輪判斷 , 直到不在或者棧中只剩 A  

最后棧內剩余的元素則是可以從上帝視角可以看到的直線個數

當然題目還有些細節要處理 : 

①、上帝視角可以看到的直線可能是位於 X 的負半軸的 , 而 t^2 是大於等於 0 的

這種情況我們只要加入一條斜率為負數 , 經過原點的直線即可 ( 答案個數 = 最后棧的大小 - 1)

②、題目可能存在兩條重合的直線 , 這種情況按題目的意思誰都不能成為唯一的第一

所以對於每條直線我們要判斷一下它被是否有重合

update :

為了方便讀者觀看我在賽后優化了代碼 , 減少了一部分不要的語句導致除了點小鍋

至於讀入讀反了能過也是神奇 hh

其中為了處理細節①所加入的直線應該滿足的條件為

①、斜率為負數

②、截距取其它直線截距的max(要覆蓋掉其它直線的負半段)

感謝 @AKDA 提出的問題 , 感謝@Overstars給出的hack數據

AC_Code

#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
#define rep(i,a,n) for (int i=a;i<=n;i++)
#define per(i,n,a) for (int i=n;i>=a;i--)
#define int long long
using namespace std;
#define ld long double
const int N = 5e5 + 10;
const ld eps=1e-20;
struct node
{
    ld k , b;
    int id , w;
} li[N];
int sta[N << 1] , top;
bool cmp(node t1 , node t2)
{
    if(t1.k == t2.k) return t1.b > t2.b;
    return t1.k > t2.k;
}
ld get(int x,int y)
{
    return (li[x].b - li[y].b) / (li[y].k - li[x].k);
}
map<pair<int , int> , int>mp;
signed main()
{
    ios::sync_with_stdio(false) , cin.tie(0) , cout.tie(0);
    int t;
    cin >> t;
    while(t --)
    {
        mp.clear();
        int n ;
        cin >> n;
        int cnt = 0 , ans = 0 , ma = -1;
        rep(i , 1 , n)
        {
            int k , b;
            cin >> b >> k;
            ma = max(ma , b);
            pair<int , int> now = make_pair(k , b);
            if(!mp[now])
            {
                mp[now] = ++ cnt;
                li[cnt].k = k , li[cnt].b = b;
                li[cnt].id = cnt , li[cnt].w = 1;
            }
            else li[mp[now]].w ++;
        }
        n ++;
        li[n].k = -1 , li[n].b = ma , li[n].w = 0;
        sort(li + 1 , li + 1 + n , cmp);
        sta[1] = 1 , top = 2;
        rep(i , 2 , n)
        {
            if(li[i].k == li[i - 1].k) continue ;
            while(top >= 3 && get(sta[top - 1] , sta[top - 2]) - get(sta[top - 1] , i) <= eps) top --;
            sta[top ++] = i;
        }
        rep(i , 1 , top - 1) if(li[sta[i]].w < 2) ans ++ ;
        cout << ans - 1 << '\n';
        rep(i , 1 , n) li[i].k = li[i].b = li[i].id = li[i].w = 0;
    }
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM