โจทย์นี้กำหนดว่าในแต่จะมีหนังสือ เล่มที่ปรากฏวันละเล่ม โดยเล่มที่ จะออกมาที่ตำแหน่ง และมีค่า
โจทย์ต้องการให้เก็บมูลค่ารวมได้มากที่สุด โดยเริ่มจากตำแหน่ง ก่อนหนังสือเล่มแรก และในแต่ละวันจะเดินทางได้ระยะ แต่จะต้องเดินทางไปเก็บหนังสือในวันนั้นเท่านั้น
แนวคิด
ข้อนี้เป็นโจทย์ Segment Tree (https://programming.in.th/tasks/1147/solution)
สำหรับโจทย์ข้อนี้หากค่าของพิกัดอยู่ในช่วงที่สมเหตุสมผลจะสามารถใช้ Segment Tree ได้โดยตรง นั่นคือสำหรับหนังสือแต่ละเล่มต้อง Query ช่วงหาค่ารวมมากสุดใน และ Update ตำแหน่ง ให้เป็นค่าที่ได้บวก ซึ่งผลรวมนี้จะแทนค่าที่มากที่สุดที่เป็นไปได้หากจบที่การเก็บเล่มที่ โดยในตอนแรกมีเพียงตำแหน่ง ที่มีมูลค่ารวม 0 และที่ตำแหน่งอื่นเป็น
แต่เนื่องจาก อาจมากถึง จึงไม่สามารถใช้ Segment Tree โดยตรงเพราะต้องใช้ Memory จึงต้องใช้ Coordinate Compression
Coordinate Compression
สังเกตว่ามีเพียง พิกัดที่จะต้องสนใจ กล่าวคือตำแหน่ง ทั้งหลายและพิกัดเริ่มต้น เพราะตำแหน่งที่เหลือไม่สามารถเป็นจุดจบของการเดินทางในแต่ละวันตามที่โจทย์ระบุ
ดังนั้นเราสามารถนำพิกัดทั้งหลายมา Sort และลบค่าที่ซ้ำ และใช้ Index ใน Array ที่ได้เพื่อเป็น Index ที่ใช้ใน Segment Tree แทนพิกัด ซึ่งจะทำให้ Segment Tree เหลืออย่างมาก ดัชนี
ตัวอย่างการทำ Coordinate Compresssion
vector<int> coords; coords.push_back(S); for (int i = 0; i < N; i++) { cin >> X[i] >> A[i]; coords.push_back(X[i]); } sort(coords.begin(), coords.end()); auto last = std::unique(coords.begin(), coords.end()); coords.erase(last, coords.end()); int C = coords.size() - 1; unordered_map<int, int> m; for (int i = 0; i < coords.size(); i++) m[coords[i]] = i;
โค้ดนี้เริ่มจากการเอาพิกัดที่ต้องการใส่ไปใน vector จากนั้น Sort และลบค่าซ้ำ สุดท้ายสร้าง unordered_map เพื่อแปลงพิกัดเดิมเป็นดัชนีใหม่
การทำ Path Compression แบบนี้จะใช้เวลา จากการ Sort
Solution
ต่อมาเมื่อทำ Coordinate Compression แล้วสามารถใช้ตำแหน่งใหม่ในการ Update / Query ค่าใน Segment Tree ดังที่อธิบายไว้ข้างบน โดยเพียงต้องแก้ที่การ Query จะต้องแก้เป็นการหาพิกัด โดย เป็นดัชนีน้อยสุดที่ทำให้ และ เป็นดัชันีมากสุดที่ ซึ่งทั้งสองสามารถใช้ lower_bound กับ upper_bound ใน STL
int l = lower_bound(coords.begin(), coords.end(), X[i] - K) - coords.begin(); int r = (upper_bound(coords.begin(), coords.end(), X[i] + K) - coords.begin()) - 1;
สำหรับ Time Complexity ในการหา lower_bound กับ upper_bound ในแต่ละชั้นใช้เวลา การ Query / Update Segment Tree ก็เช่นกัน ดังนั้นทั้งหมดจึงเป็น
โค้ดเต็มที่เอาขั้นตอนที่อธิบายไว้มารวมกัน
#include <algorithm> #include <vector> #include <iostream> #include <unordered_map> using namespace std; // Segment Tree int ST[100100 * 4 + 100]; int update(int i, int Z, int n, int l, int r) { if (l == i && i == r) { ST[n] = Z; return ST[n]; } if (r < i || i < l) return ST[n]; int mid = (l + r) / 2; int new_left_value = update(i, Z, n * 2, l, mid); int new_right_value = update(i, Z, n * 2 + 1, mid + 1, r); ST[n] = max(new_left_value, new_right_value); return ST[n]; } int query(int A, int B, int n, int l, int r) { if (A <= l && r <= B) return ST[n]; if (B < l || r < A) return -1000000001; // -inf int mid = (l + r) / 2; int left_query = query(A, B, n * 2, l, mid); int right_query = query(A, B, n * 2 + 1, mid + 1, r); return max(left_query, right_query); } int X[100100]; int A[100100]; int M[100100]; int main() { int N, K, S; cin >> N >> K >> S; vector<int> coords; coords.push_back(S); for (int i = 0; i < N; i++) { cin >> X[i] >> A[i]; coords.push_back(X[i]); } sort(coords.begin(), coords.end()); auto last = std::unique(coords.begin(), coords.end()); coords.erase(last, coords.end()); int C = coords.size() - 1; unordered_map<int, int> m; for (int i = 0; i < coords.size(); i++) m[coords[i]] = i; for (int i = 0; i <= C; i++) update(i, -1000000010, 1, 0, C); update(m[S], 0, 1, 0, C); int ans = 0; for (int i = 0; i < N; i++) { int l = lower_bound(coords.begin(), coords.end(), X[i] - K) - coords.begin(); int r = (upper_bound(coords.begin(), coords.end(), X[i] + K) - coords.begin()) - 1; int M = query(l, r, 1, 0, C) + A[i]; ans = max(ans, M); update(m[X[i]], M, 1, 0, C); } cout << ans; }