NC14301 K-th Number(二分+尺取)

K-th Number

https://ac.nowcoder.com/acm/problem/14301

题意

有T组数据。
每组数据给定长度为 的数组 ,对所有长度大于等于 的连续子段,取出其第 大放入数组 中。求数组 的第 大。

分析

先吐槽一下,第 大表意真的不明啊!!
这题简直是套路套路套路题。前几天刚做过类似的。。。
考虑二分答案然后检查可行性。(没做过的话真难想啊!!
我们不妨设 ,看看满足这个条件的情况是什么。其实就是数组大于等于 的至少有 个。
现在我们就要求 数组大于等于 的数有多少个,即有多少个区间的第 大大于等于
然后我们让:

  1. 时,
  2. 时,

的前缀和。
当区间 的和大于等于 时,说明这个区间有至少个大于等于 的数,那么这个区间的第 大一定大于等于
因此,假设 为右端点,那么对于所有 区间 都能满足该条件。
一开始想的是对于每个 ,用树状数组求 有多少个,属实智障。
再想想,当某个 满足时, 一定也都满足,我们找到最大的 就可以了。然后 参与的区间个数就是 了。
然后对于每个 ,其所对应的最大 一定是单调不减的。我们从 扫一遍即可。
复杂度是
还有个细节, 要开 !!!居然因为这个 了好一会儿。。

代码如下

#include <bits/stdc++.h>
#define N 250005
using namespace std;
typedef long long LL;
LL z = 1;
LL read(){
    LL x, f = 1;
    char ch;
    while(ch = getchar(), ch < '0' || ch > '9') if(ch == '-') f = -1;
    x = ch - '0';
    while(ch = getchar(), ch >= '0' && ch <= '9') x = x * 10 + ch - 48;
    return x * f;
}
int a[N], s[N], n, k;
LL m;
int ok(int x){
    int i, j, l = 0;
    LL sum = 0;
    for(i = 1; i <= n; i++) s[i] = s[i - 1] + (a[i] >= x);
    for(i = k; i <= n; i++){
        while(s[i] - s[l] >= k) l++;
        sum += l;
    }
    return sum >= m;
}
int main(){
    int i, j, T, l, r, mid;
    T = read();
    while(T--){
        n = read(); k = read(); m = read();
        l = r = 1;
        for(i = 1; i <= n; i++) a[i] = read(), r = max(r, a[i]);
        while(l < r){
            mid = l + r + 1 >> 1;
            if(ok(mid)) l = mid;
            else r = mid - 1;
        }
        printf("%d\n", l);
    }
    return 0;
}
每日一题 文章被收录于专栏

牛客的每日一题(换卫衣之路(bushi

全部评论
不开long long见祖宗,我见到了太爷了
点赞 回复 分享
发布于 2023-08-12 23:13 湖南
为什么这个m一定得要longlong啊,m的范围最大不应该是1e5吗
点赞 回复 分享
发布于 2021-10-04 10:58
判断函数里面for循环加while循环,这样时间复杂度应该是O(n²logV)吧,用尺取更快!
点赞 回复 分享
发布于 2021-07-25 09:27

相关推荐

08-07 20:06
中南大学
点赞 评论 收藏
分享
06-26 15:33
青岛工学院 Java
积极的秋田犬要冲国企:他现在邀请我明天面试
点赞 评论 收藏
分享
评论
21
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务