每日一题(牛客)— 2020 - 04 - 22

题目链接

二分答案,一脸懵逼,感觉大佬都太强了,自己都想不到,看了大佬们的代码,学习了一下思想和知识点(二分没错,今天又进新坑)

解题思路:

  • 首先这个二分就很不好想(对于自己),这个二分,他求的是对于一个数 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]);
	}
}
发布了121 篇原创文章 · 获赞 7 · 访问量 4307

猜你喜欢

转载自blog.csdn.net/LiangNiuMu/article/details/105703963
今日推荐