ในข้อนี้หากเราพิจารณาค่า ใด ๆ โดยนิยามให้ หมายถึงฟังก์ชันแทนความถี่ของ จะสังเกตว่า เสมอ เพราะถ้าคู่อันดับ ใด ๆ ถูกนับรวมใน แล้วแปลว่า แล้วคู่อันดับนั้นก็ย่อมถูกนับรวมใน ด้วย เพราะ จากเหตุผลดังกล่าวจะได้ว่า ฟังก์ชัน เป็นฟังก์ชัน monotone ดังนั้นการหาค่า ที่มากที่สุดที่เป็นคำตอบสามารถหาได้ด้วยการ binary search ค่า โดยตรง
ระหว่างการทำ binary search หากกำลังพิจารณาคำตอบในช่วง อยู่โดย mid = (l+r)/2
จะมีกรณีที่ต้องพิจารณาเพื่อเปลี่ยนช่วงคำตอบ 2 กรณีดังนี้
- นั่นหมายความว่าคำตอบที่ต้องการนั้นมีค่ามากกว่าหรือเท่ากับ
mid
แน่นอน จึงเปลี่ยนช่วงไปพิจารณาช่วง ต่อไป - นั่นหมายความว่าคำตอบที่ต้องการนั้นน้อยกว่า
mid
แน่นอน จึงเปลี่ยนช่วงไปพิจารณาช่วง ต่อไป
วิธีในการคำนวณ สามารถทำได้ตรง ๆ ด้วยการไล่นับคู่อันดับ ที่ ได้แต่วิธีนี้มี Time complexity ต่อการคำนวณ 1 ครั้ง และเมื่อรวมจำนวนครั้งที่คิด โดยจากการ binary search จำเป็นต้องคิด ทั้งหมด ครั้ง ดังนั้นจึงได้ Time complexity ซึ่งไม่ทันเวลา วิธีการ optimize นั้นเริ่มได้จากการ preprocess prefix sum ของทุก ๆ index ที่ โดยนิยามให้ ผลรวมของ จากการ preprocess prefix sum จะได้ว่า ดังนั้นคู่อันดับ ที่เราต้องการนั้นจะมีคุณสมบัติคือ หรือก็คือ
โดยวิธีในการคำนวณ ใด ๆ นั้นเริ่มได้จากการพิจารณาตั้งแต่ โดยสำหรับ ค่าหนึ่งจะนิยามให้ หมายถึงฟังก์ชันแทนจำนวนคู่อันดับ ที่ และ เราสามารถคำนวณ โดยเริ่มจากการกำหนด array โดยนิยามให้ หมายถึงจำนวนของค่า และในตอนเริ่มต้น ในทุก ๆ จากนั้น update array ใน index ด้วยการเพิ่มค่าช่องนั้น ๆ ไป 1 การทำแบบนี้ทำให้เราสามารถคำนวณค่า ได้สำหรับทุก ๆ ด้วยการหาผลรวม ดังนั้น หากพิจารณา แล้วนำคำตอบมารวมกัน จะได้เป็นความถี่ของ นี่เรากำลังค้นหาอยู่
เนื่องจากค่า นั้นอาจมีค่ามากหรือติดลบได้ เราจึงนำเทคนิค coordinate compression มาช่วย โดยเทคนิคนี้จะเก็บข้อมูลไว้ใน vector coord
และใช้การ sort ข้อมูลทั้งหมด และนำค่าซ้ำออก เมื่อค่าใด ๆ ถูกเรียกใช้งานจะแทนค่าค่านั้นด้วย index ใน vector coord
แทน ดังนั้นปัญหาค่าติดลบหรือมากเกินว่าจะนำไปเป็น index ของ array จะหมดไป
for (int i = 1; i <= n; i++) coord.emplace_back(pref[i]); //Coordinate compression coord.emplace_back(0); // base case sort(coord.begin(), coord.end()); coord.resize(unique(coord.begin(), coord.end()) - coord.begin());
การ update ค่านั้นหากนำค่าไปใส่ในช่องตรง ๆ ด้วย time complexity และ query ไล่ผลรวมตั้งแต่ ถึง ด้วย time complexity จะทำให้ time complexity ต่อการดำเนินการต่อ j ใด ๆ เป็น และ time complexity ในการคิด 1 ครั้งเป็น และเมื่อคิดการคำนวณทุก ๆ ครั้งจะมี time complexity เป็น เหมือนเดิม ด้วยการใช้โครงสร้างข้อมูล fenwick tree สามารถ optimize time complexity ทั้ง update และ query ต่อการดำเนินการ 1 ครั้งเป็น ในการคิด 1 ครั้งจะลด time complexity เหลือเพียง และเมื่อรวมเวลาการคำนวณทุกครั้งได้ time complexity เป็น
void update(int x, long k) { for (int i = x; i < N; i += i & -i) t[i] += k; } long long query(int x, long ret = 0) { for (int i = x; i; i -= i & -i) ret += t[i]; return ret; } int F(int mid) { long long now = 0; memset(t, 0, sizeof t); update(get(0), 1); for (int i = 1; i <= n; i++) { int pos = lower_bound(coord.begin(), coord.end(), pref[i] - mid) - coord.begin() + 1; if (coord[pos - 1] > pref[i] - mid) --pos; if (pos) now += query(pos); update(get(pref[i]), 1); } return now; }