題目鏈接
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; }