ข้อนี้ให้ Array และให้หาจำนวน Subarray ที่มีพิสัย (ค่ามากสุด - ค่าต่ำสุด) อยู่ในช่วง
แนวคิด
ข้อนี้เป็นโจทย์ Sliding Window
อย่างแรกสังเกตว่าเราสามารถคำนวณจำนวนลำดับย่อยที่มีพิสัยในช่วง เป็น (จำนวนลำดับย่อยที่มีพิสัยไม่เกิน ) - (จำนวนลำดับย่อยที่มีพิสัยไม่เกิน ) ดังนั้นสำหรับข้อนี้เราจะพิจารณาการหาจำนวนลำดับย่อยที่มีพิสัยไม่เกิน
ในจำนวนลำดับย่อยที่มีพิสัยไม่เกิน เราสามารถพิจารณาจำนวนลำดับย่อยที่เข้าข่ายที่จบที่แต่ละ หากนำจำนวนนี้มาบวกกันสำหรับทุก จะได้คำตอบที่ต้องการ
สังเกตว่าสำหรับ ถ้า Subarray มีพิสัยไม่เกิน จะได้ว่า มีพิสัยไม่เกิน เช่นกัน (เพราะค่ามากสุดจะไม่เกินและค่าต่ำสุดจะไม่น้อยกว่าของ Subarray ที่เริ่มที่ ) ดังนั้นสำหรับแต่ละ เราเพียวต้องหาค่า ที่เป็นค่าที่ต่ำสุดที่ทำให้ มีพิสัยไม่เกิน กล่าวอีกแบบคือ
สังเกตได้อีกว่าสำหรับ จะได้ว่า ทั้งนี้เพราะถ้า มีพิสัยไม่เกิน ซึ่งเป็น Subarray จะต้องมีพิสัยในช่วงนั้นเช่นกัน
แนวคิดหลักของข้อนี้คือเราสามารถใช้วิธี Sliding Window ในการหาแต่ละ โดยจะใช้ Sliding Window สำหรับค่า max หนึ่งอันและ Sliding Window สำหรับ min อีกอัน (ผู้ที่ไม่คุ้นกับ Sliding Window สามารถอ่านได้จาก https://programming.in.th/tasks/1157/solution) โดย Sliding Window จะสามารถช่วยเราหาค่า max และ min ใน Window ที่เลื่อนไปทางขวาเรื่อยๆ ได้ในเวลา Amortized สำหรับทั้ง ช่องใน Array
เราจะ Initialize ซึ่งแทนจะ ในแต่ละขั้นเป็น 0 จากนั้นสำหรับแต่ละ ถึง ในแต่ละขั้นจะเริ่มจากการ push ดัชนี เข้าไปใน Window ทั้งสอง เพิ่ม และ pop ดัชนีใน Sliding Window ที่อยู่ก่อน จน เมื่อได้ ที่ถูกต้องแล้วจะได้ว่าจำนวน Subarray ที่มีพิสัยไม่เกิน ที่จบที่ คือ
เนื่องจากการ Iterate ทุกค่าและใช้ Sliding Window ทั้งสองจะใช้เวลา ข้อนี้จึงใช้เวลาทั้งหมด
ตัวอย่างโค้ด
long long range_leq_q(int q, const vector<int> &a) { deque<int> d_max; deque<int> d_min; long long res = 0; long long l = 0; for (int i = 0; i < a.size(); i++) { while (d_max.size() > 0 && a[d_max.back()] <= a[i]) d_max.pop_back(); d_max.push_back(i); while (d_min.size() > 0 && a[d_min.back()] >= a[i]) d_min.pop_back(); d_min.push_back(i); while (l <= i && a[d_max[0]] - a[d_min[0]] > q) { l++; while (d_max.size() > 0 && d_max[0] < l) d_max.pop_front(); while (d_min.size() > 0 && d_min[0] < l) d_min.pop_front(); } res += (i - l + 1); } return res; }
นี่คือโค้ดตัวอย่างสำหรับการหาจำนวน Subarray ที่มีพิสัยไม่เกิน
เราจะเก็บ Sliding Window สองอันคือ d_min
กับ d_max
และเก็บค่า ที่อธิบายไดว้
ในแต่ละขั้นจะ push เข้า Sliding Window ทั้งสองโดยเอาข้อมูลที่สำคัญแล้วออก (ต่ำกว่าค่าปัจจุบันสำหรับ d_max
หรือ สูงกว่าค่าปัจจุบันสำหรับ d_min
)
จากนั้นจะเพิ่มค่า และ pop ค่าที่อยู่ก่อน ในแต่ละ Window จนพิสัยไม่เกิน