快轉到主要內容

CF Edu Binary Search -- Step 5 pC K-th Sum

目錄

題目
#

有兩個陣列 \(a\) 和 \(b\),各自包含 \(n\) 個正整數。對於每一組 \((i, j)\) \((1 \leq i, j \leq n)\),將 \(a_i + b_j\) 放到陣列中。請求出此陣列中第 \(k\) 小的數字。

解題思路
#

按照題目暴力模擬時間複雜度 \(O(n^2)\),不太可行,我們可以試試對答案二分搜。

對於一個答案 \(ans\),將 \(a\) 和 \(b\) 排序好後,用雙指針, \(p1\) 指向 \(1\)、 \(p2\) 指向 \(n\)。當 \(p1\) 增加的時候,重複 p2-- 直到 \(a_{p1} + b_{p2} \leq ans\)。維護一個變數 \(cnt\),代表有多少個配對 \(\leq ans\)。每次改變完 \(p2\) 後, \(cnt\) 就加上 \(p2\)。最後檢查 \(cnt\) 是否 \(\geq k\) 後縮小範圍區間。

常見錯誤
#

  • 要記得 \(k\) 和 \(cnt\) 要開 long long,不然有機會 overflow
  • \(l\) 和 \(r\) 有些寫法 (例如 \(m = (l + r) / 2\))需要開 long long
  • 大於 / 大於等於 / 小於 / 小於等於 要弄對
  • 上面的解題思路是 1 based,但是範例程式碼是 0 based,有些地方要多 \(+1\)

題外話
#

這題和 TOI 2023 的 pB 裁員風暴 很像,寫完這題可以寫寫看

完整程式碼
#

#include <bits/stdc++.h>

using namespace std;

signed main(){
    long long n, k;
    cin >> n >> k;
    vector<int> a(n), b(n);
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }
    for (int i = 0; i < n; i++) {
        cin >> b[i];
    }
    sort(a.begin(), a.end());
    sort(b.begin(), b.end());
    int l = 0, r = 2e9;
    while (l < r) {
        int m = l + (r - l) / 2;
        int p2 = n - 1;
        long long cnt = 0;
        for (int p1 = 0; p1 < n; p1++) {
            while (p2 != -1 && a[p1] + b[p2] > m) {
                p2--;
            }
            cnt += p2 + 1;
        }
        if (cnt < k) {
            l = m + 1;
        }
        else{
            r = m;
        }
    }
    cout << l << "\n";
}
Piau 的筆記本
作者
Piau 的筆記本
希望我寫下來的東西能夠長久的記在我的腦中