Kickstart Round D 2018 B題 Paragliding
題目大意:有N個塔,水平座標為 p[i], 高度為 h[i], 每個塔的水平座標各不相同。 有K個氣球,每個氣球可看做一個點,座標為x[i], y[i]。一個人可以爬到每個塔的任意高度位置,然後在該位置可以向左右45度滑行,滑行的軌跡是直線。如果在途中遇到氣球,則可獲得該氣球,如果氣球和塔相重疊,也認為可獲得氣球,要求一共可以獲得多少個氣球。
解題關鍵點:
1. 如果從一個塔( p[j], h[j] )向下滑行可以獲得某個氣球(x[i], y[i]), 則滿足 abs(x[i] - p[j] ) + y[i] <= h[j]
2. 如果一個塔 i 和另外一個塔 j 滿足 abs(p[i] - p[j]) + h[i] <= h[j], 則第i個塔是多餘的,因為任意從第i個塔滑行獲得的氣球,都可以通過從第j個塔滑行獲得。
解題方法:
1. 小資料: 將每個氣球和所有的塔進行關鍵點1條件比較,如果滿足,則該氣球可獲得。
2. 大資料: 對於每個氣球,如果我們能找到能它最近的塔,只判斷是否能從離它最近的塔上獲得就好了。但是存在這樣一個問題,如果從離它最近的塔上不能獲得,也許存在離它較遠位置的塔,從這些塔上獲得氣球。這樣的話,還得依次遍歷所有的塔。為了達到最初的想法,考慮關鍵點2, 如果我們把所有多餘的塔去掉,那麼就可以只判斷是否能從離它最近的塔上獲得就可以了。為什麼? 假設某個氣球座標為(x, y) , 離它最近的右邊的塔為(p1, h1),從該塔不能獲得氣球,則滿足 p1 - x + y > h1, 該式子等於 -p1 + x - y + h1 < 0, 又假設存在離該氣球更遠的塔(p2, h2 ) p2 >p1, 從該塔上可以獲得氣球,則滿足 p2 - x + y <= h2, 將這個式子和前面的式子相加得到 p2 - p1 + h1 <= h2,正好關鍵點2中的條件,也就是說離它最近的那個塔是多餘的。 所以,我們把所有多餘的塔去掉之後,就可以找到離某個氣球最近的塔,找的過程可以將所有的塔從小到大排好序,再使用二分搜尋進行查詢。
程式碼:
#include <iostream> #include <iostream> #include <string.h> #include <iomanip> #include <algorithm> #include <stdio.h> #include <map> #include <set> #include <queue> #include <stack> using namespace std; typedef long long ll; const int maxn = 100010; ll p[maxn], h[maxn], x[maxn], y[maxn]; ll relevantT[maxn]; ll towerSorted[maxn];//排好序的塔 map<ll, int> valueAndID; //塔的位置和索引 ll A[5], B[5], C[5], M[5]; int T, N, K; int getRelation(int i, int j)//第i個塔, 第j個塔 i > j { if(h[j] >= p[i] - p[j] + h[i]) //第i個塔是多餘的 return 1; if(h[i] >= p[i] - p[j] + h[j]) //第j個塔是多餘的 return 2; return 3; } bool ok(int i, int j)//第i個氣球,第j個塔 { if(abs(x[i] - p[j]) + y[i] <= h[j]) return true; return false; } int getRelevantTowers() { for(int i = 1; i <= N; i++) { towerSorted[i] = p[i]; valueAndID[p[i]] = i; } sort(towerSorted + 1, towerSorted + 1 + N); stack<ll> st; for(int i = 1; i <= N; i++) { if(st.empty()) { st.push(towerSorted[i]); continue; } int relation = getRelation(valueAndID[towerSorted[i]], valueAndID[st.top()]); if(relation == 1)//多餘的 continue; if(relation == 2)//棧頂的塔是多餘的 { st.pop(); while(!st.empty() && getRelation(valueAndID[towerSorted[i]], valueAndID[st.top()]) == 2) { st.pop(); } } st.push(towerSorted[i]); } int len = st.size(); int tp = len; while(!st.empty()) { relevantT[tp--] = st.top(); st.pop(); } return len; } int main() { freopen("B-large-practice.in", "r", stdin); freopen("out2.txt", "w", stdout); cin >> T; for(int cas = 1; cas <= T; cas++) { valueAndID.clear(); cin >> N >> K; cin >> p[1] >> p[2] >> A[1] >> B[1] >> C[1] >> M[1]; for(int i = 3; i <= N; i++) p[i] = (A[1] * p[i - 1] + B[1] * p[i - 2] + C[1]) % M[1] + 1; cin >> h[1] >> h[2] >> A[2] >> B[2] >> C[2] >> M[2]; for(int i = 3; i <= N; i++) h[i] = (A[2] * h[i - 1] + B[2] * h[i - 2] + C[2]) % M[2] + 1; cin >> x[1] >> x[2] >> A[3] >> B[3] >> C[3] >> M[3]; for(int i = 3; i <= K; i++) x[i] = (A[3] * x[i - 1] + B[3] * x[i - 2] + C[3]) % M[3] + 1; cin >> y[1] >> y[2] >> A[4] >> B[4] >> C[4] >> M[4]; for(int i = 3; i <= K; i++) y[i] = (A[4] * y[i - 1] + B[4] * y[i - 2] + C[4]) % M[4] + 1; int len = getRelevantTowers(); int ans = 0; for(int i = 1; i <= K; i++) { int L = lower_bound(relevantT + 1, relevantT + 1 + len, x[i]) - relevantT; //和塔重疊 if(x[i] == relevantT[L]) { if(ok(i, valueAndID[relevantT[L]])) ans++; continue; } //該氣球在最左邊 if(L == 1) { if(ok(i, valueAndID[relevantT[L]])) ans++; continue; } //該氣球在最右邊 if(L > len) { if(ok(i, valueAndID[relevantT[len]])) ans++; continue; } //該氣球處在兩個塔之間 int LL = L - 1; int RR = L; if(ok(i, valueAndID[relevantT[LL]])) { ans++; continue; } if(ok(i, valueAndID[relevantT[RR]])) ans++; } cout << "Case #" << cas <<": " << ans <<endl; } return 0; }