Subtask 1 (4 คะแนน)
เนื่องจาก ไม่ว่าเราจะเพิ่มหรือลดช่วงอย่างไรจะได้ว่า เสมอ ดังนั้น
Time Complexity:
Subtask 2 (9 คะแนน)
ในปัญหาย่อยนี้สามารถใช้วิธีการ Brute Force ในการไล่ตรวจสอบทุกช่วงที่เป็นไปได้จากนั้นนำมา sort แล้วหาลำดับที่ K เพื่อตอบได้
Time Complexity:
Subtask 3 (7 คะแนน)
ในปัญหาย่อยนี้สิ่งหลักที่ต้องสังเกตคือจะมีแร่ ที่คุ้มค่าที่สุด นั่นคือแร่ที่มี มากที่สุด และหากเพิ่มแร่ชนิดอื่นเข้าไปจะทำให้มูลค่าลดลงเสมอ สมมติว่าเราต้องการเพิ่มแร่ ให้ขุดพร้อมกับแร่ เนื่องจากแร่ คุ้มค่าที่สุด เราจะได้ว่า ซึ่งเมื่อย้ายข้างอสมการและบวก ไปทั้งสองข้างจะได้ว่า เป็นจริง และได้ว่า เป็นจริงด้วย ดังนั้นเราสามารถไล่หาแร่ที่คุ้มค่าที่สุด และตอบมูลค่าของแร่นั้นได้เลย
Time Complexity:
Subtask 4, 5 (12 + 11 คะแนน)
จากข้อสังเกตในปัญหาย่อยที่ 3 จะได้ว่าค่าที่มากที่สุดลำดับที่ จะมีแร่อยู่ไม่เกิน ชนิด ดังนั้นเราสามารถไล่ Brute Force และ bound ให้ช่วงมีขนาดไม่เกิน ได้ แต่ต้องมีการ optimize memory เพื่อไม่ให้ใช้ memory เยอะจนเกินไป เนื่องจากปัญหาต้องการค่าที่มากที่สุด ลำดับที่ จึงสามารถใช้ std::priority_queue
ในการ maintain จำนวนที่มากที่สุด อันดับแรกได้ และหากใน priority_queue มีสมาชิกมากกว่า ตัวก็สามารถ pop สมาชิกที่มีค่าน้อยที่สุดออกได้
Time Complexity:
Subtask 6 (20 คะแนน) เท่ากันหมด,
ในการแก้ปัญหานี้เราจะทำการ Binary Search บนคำตอบหรือก็คือมูลค่าที่มากที่สุดลำดับที่ K ให้มูลค่านั้นเป็น หากเราเลือก ใด ๆ จะได้ว่า จะเป็นลำดับลดลงตาม ที่เพิ่มขึ้น ดังนั้นเราสามารถ binary search อีกครั้งเพื่อหาว่าหากเลือกค่า L แล้ว สามารถเลือกค่า ได้มากที่สุดเท่าไหร่จึงจะไม่น้อยกว่า x จากนั้นบวกคำตอบของทุก ๆ ค่า ว่าจำนวนช่วงที่เป็นไปได้มากกว่าหรือเท่ากับ หรือไม่ หากมากกว่าก็สามารถให้ เป็นคำตอบได้หรือหากน้อยกว่าก็ไม่สามารถให้ เป็นคำตอบได้
Time Complexity:
Official Solution
เราจะทำการ Binary Search บนคำตอบของปัญหาข้อนี้ โดยเราจะพิจารณาว่า สามารถเป็นคำตอบของปัญหานี้ได้ก็ต่อเมื่อมีช่วง [L, R] ซึ่งเป็นค่าที่มากที่สุดลำดับที่ บางช่วงที่สอดคล้องกับสมการ
และเนื่องจาก ดังนั้นเราสามารถย้ายข้างอสมการและได้ว่า ซึ่งเมื่อแทนค่าในอสมการเป็น จะสามารถเขียนอสมการดังกล่าวใหม่ให้อยู่ในรูป ได้ ดังนั้นเราลดรูปปัญหาให้อยู่ในรูปของการหาช่วง ที่เป็นช่วงมากสุดลำดับที่ ของ และตรวจสอบว่า หรือไม่
ในการหาว่ามีช่วงดังกล่าวอยู่หรือไม่ เราจะทำการหาว่ามีช่วงที่มากกว่าหรือเท่ากับ อยู่ทั้งหมดกี่ช่วง จากนั้นเทียบกับค่า จะทำให้รู้ว่าช่วงที่มากที่สุดลำดับที่ มากกว่าหรือน้อยกว่า นั่นเอง โดยเมื่อเราแทนให้ แทน prefix sum ของ แล้วช่วง จะมีค่ามากกว่าหรือเท่ากับ ก็ต่อเมื่อ และ เท่านั้นซึ่งปัญหานี้ก็จะตรงกับปัญหา classic อย่างการ count inversion ที่ใช้ divide and conquer ในลักษณะเดียวกันกับ merge sort algorithm ในการแก้ปัญหาได้ใน
Time Complexity:
Alternative Solution
อีกวิธีในการแก้ปัญหา count inversion สามารถใช้ Fenwick Tree หรือ Binary Indexed Tree มาช่วยในการแก้ไขปัญหานี้ได้ โดย Fenwick Tree เป็นเนื้อหาที่อยู่ในค่ายสสวท. ค่าย 1 แต่สามารถแก้ปัญหานี้ได้ใน Time Complexity ซึ่งเท่ากับการเขียน merge sort
Time Complexity:
#include <bits/stdc++.h> using namespace std; long long A[100001], B[100001], X[100001]; long long cnt_inv = 0; void merge(int l, int r, int mid){ vector<long long> L, R; for(int i = l; i <= mid; i++) L.push_back(X[i]); for(int i = mid + 1; i <= r; i++) R.push_back(X[i]); int lidx = 0, ridx = 0, idx = l; while(lidx < L.size() && ridx < R.size()){ if(L[lidx] > R[ridx]) X[idx++] = L[lidx++]; else{ cnt_inv += (L.size() - lidx) * 1ll; X[idx++] = R[ridx++]; } } while(ridx < R.size()) X[idx++] = R[ridx++]; while(lidx < L.size()) X[idx++] = L[lidx++]; } void count_inversion(int l, int r){ if(l == r) return; int mid = (l + r) >> 1; count_inversion(l, mid); count_inversion(mid + 1, r); merge(l, r, mid); } int main(int argc, char* argv[]){ long long N, K; scanf("%lld %lld", &N, &K); for(int i = 1; i <= N; i++){ scanf("%lld", &A[i]); A[i] *= (1000 * 1ll); } for(int i = 1; i <= N; i++) scanf("%lld", &B[i]); long long L = 1, R = 1e7; while(L < R){ long long mid = (L + R + 1) >> 1; X[0] = 0; for(int i = 1; i <= N; i++) X[i] = A[i] - (mid * B[i]) + X[i - 1]; cnt_inv = 0; count_inversion(0, N); if(cnt_inv >= K) L = mid; else R = mid - 1; } double ans = double(L) / double(1000); printf("%.3lf", ans); return 0; }