K-th Number 二分

題目鏈接

題意

n個元素的數組A,從A數組中所有長度大於等於k的區間中取出第k大放入B數組中,求B數組的第m大。
思路
這題正常想很難搞,看了題解才懂。mark一下。
考慮二分的做法,問題是求B數組第m大的是誰,假設爲ans,考慮一個值x,求B數組裏大於等於x的值是否大於m個,這個問題具有單調性,即求A數組裏區間第k大的數大於等於x的區間是否多於m個。然而這個問題也不好解,神犇的做法來了,將數組A裏大於等於x的值賦爲1,否則爲0,只要一個區間的和大於等於k,那麼這個區間第k大的數就大於等於x,然後就是求有多少個區間和大於等於k了,考慮固定了區間右端點j,當左端點爲i,[i,j]的和大於等於k時,那麼左端點小於i的也肯定滿足條件,那麼只要尺取法掃一遍數組就行。最後判斷區間和大於等於k的個數是不是多於m就行。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 7;
typedef long long ll;
int n, k, a[maxn], sum[maxn];
ll m;
bool check(int x) {
    ll ans = 0;
    for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + (a[i] >= x);
    int last = 0;
    for (int i = k; i <= n; i++) {
        while (sum[i] - sum[last] >= k) last++;
        ans += last;
    }
    return ans >= m;
}
int main()
{
    int t;
    scanf("%d", &t);
    while (t--) {
        scanf("%d%d%lld", &n, &k, &m);
        int left = 1, right = 1, ans;
        for (int i = 1; i <= n; i++) scanf("%d", &a[i]), left = min(left, a[i]), right = max(a[i], right);
        while (left <= right) {
            int mid = (left + right) / 2;
            if(check(mid)) {
                ans = mid;
                left = mid + 1;
            }
            else right = mid - 1;
        }
        printf("%d\n", ans);
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章