二分答案,一脸懵逼,感觉大佬都太强了,自己都想不到,看了大佬们的代码,学习了一下思想和知识点(二分没错,今天又进新坑)
解题思路:
- 首先这个二分就很不好想(对于自己),这个二分,他求的是对于一个数 x, 有多少个符合条件满足的,如果满足的个数 res >= m, 那么这个 x 肯定是选取小了,否则就是选取大了。
- 然后我们如何去控制他的左右边界,这里看大佬的代码学到个技巧,让他们排序,然后去重(原先学过,不过忘了),然后求出他的长度(也就是有几个值),因为我们肯定是从这个数组 b 里去选值。
- 然后我们就进行常规二分,这里的二分关键在与怎么求出res (符合条件的个数),这个也是看大佬的思想的,自己没想到的。首先我们排着遍历,直到符合条件的数 == k, 那么我们计算后面有多少个值,因为后面的扩大区间也一定满足,也就是n - i + 1,然后这里的关键是,我们从前面开始while(一开始的位置是1),因为只要他 < x,那么我们后面的长度仍然成立,所以这么循环下去就OK,最后输出值即可。
注意:
- 一个是 M 爆int ,因为随便举个例子,n 是 1e5,如果是n 个里面选取两个,那么情况坏的时候会达到1e10,那么就会爆,所以开long long(重点)
- 还有就是我们不是对l , r进行处理,是对b[mid] 这个值进行二分查找,也就是二分查找 b 数组。
代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010;
long long m;
int n, k;
int a[N], b[N];
bool check(int x){
long long res = 0;
int cnt = 0;
int pos = 1;
for (int i = 1; i <= n; i ++){
if (a[i] >= x){
cnt ++;
}
if (cnt == k){
res = res + n - i + 1;
while(a[pos] < x){
pos++;
res = res + n - i + 1;
}
pos ++;
cnt --;
}
}
if (res >= m) return true;
else return false;
}
int main(){
int t;
scanf("%d",&t);
while(t--){
scanf("%d%d%lld",&n,&k,&m);
for (int i = 1; i <= n; i ++){
scanf("%d",&a[i]);
b[i] = a[i];
}
sort(b + 1, b + 1 + n);
int len = unique(b + 1, b + 1 + n) - b - 1;
int l = 1, r = len;
while(l < r){
int mid = (l + r + 1) >> 1;
if (check(b[mid])) l = mid;
else r = mid - 1;
}
printf("%d\n",b[l]);
}
}