【每日一题】20.K-th Number (二分 + 尺取)

关于此题,我们分析一下:
一个区间第k大的数不小于x的条件是什么?

答案就是一个区间内不小于x的数的个数不小于k

那么,我们就会发现,我们其实并不需要知道每个数的值,实际上对我们有用的只有每个数与x的大小关系,然后,我们就可以直接用贡献法计算。

我们把所有值不下于x的赋为1,剩下的赋为0,那么,二分求的东西就被转换成了:

有多少个区间的区间和不下于k,且序列里面的值只可能是0或1

然后随便搞个前缀和加个单调指针就行了

(其实可以不用单调指针直接用桶存的,常数更小,但是,由于脑袋有点晕,出锅了,就懒得改了)

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 1;
int a[N], b[N];
int n, k, m;
inline int calc(int x) {
    for (int i = 1; i <= n; ++i) {
        b[i] = (a[i] >= x);
        b[i] += b[i - 1];
    }
    int ans = 0, l = 1;
    for (int i = 1; i <= n; ++i) { //枚举右端点
        while (b[i] - b[l - 1] >= k) ++l;
        ans += (l - 1);
    }
    return ans;
}
inline int read() {
    char ch = getchar();
    int w = 0, ss = 0;
    while (ch < '0' || ch > '9') w |= (ch == '-'), ch = getchar();
    while (ch >= '0' && ch <= '9') ss = (ss * 10 + (ch - '0')), ch = getchar();
    return w ? -ss : ss;
}
signed main() {
    int T = read();
    while (T--) {
        n = read(), k = read(), m = read();
        for (int i = 1; i <= n; ++i) a[i] = read();

        int l = 1, r = 1e9, ans = 0;
        while (l <= r) {
            int mid = (l + r) >> 1;
            int tot = calc(mid); //区间kth大于等于mid的有几个
            if (tot >= m) {
                ans = mid;
                l   = mid + 1;
            } else
                r = mid - 1;
        }
        printf("%lld\n", ans);
    }
    return 0;
}

另外一种写法

#include <cstdio>
#include <iostream>
using namespace std;
const int N = 1e5;
int a[N + 2], s[N + 2];
int main() {
    int T, n, i, j, K, l, r, mid;
    long long m, res;
    scanf("%d", &T);
    while (T--) {
        scanf("%d%d%lld", &n, &K, &m);
        for (i = r = 1, l = 1e9; i <= n; i++) {
            scanf("%d", &a[i]);
            l = min(l, a[i]);
            r = max(r, a[i]);
        }
        while (l < r) {
            mid = l + r >> 1;
            for (i = 1, res = j = 0; i <= n; i++) {
                s[i] = s[i - 1] + (a[i] > mid);
                while (j < i && s[j] <= s[i] - K) j++;
                res += j;
            }
            res >= m ? l = mid + 1 : r = mid;
        }
        printf("%d\n", l);
    }
    return 0;
}
posted @ 2021-05-08 09:59  RioTian  阅读(60)  评论(0编辑  收藏  举报