题意
有T组数据。
每组数据给定长度为 的数组
,对所有长度大于等于
的连续子段,取出其第
大放入数组
中。求数组
的第
大。
分析
先吐槽一下,第 大表意真的不明啊!!
这题简直是套路套路套路题。前几天刚做过类似的。。。
考虑二分答案然后检查可行性。(没做过的话真难想啊!!
我们不妨设 ,看看满足这个条件的情况是什么。其实就是
数组大于等于
的至少有
个。
现在我们就要求 数组大于等于
的数有多少个,即有多少个区间的第
大大于等于
。
然后我们让:
时,
时,
记 为
的前缀和。
当区间 的和大于等于
时,说明这个区间有至少
个大于等于
的数,那么这个区间的第
大一定大于等于
。
因此,假设 为右端点,那么对于所有
的
区间
都能满足该条件。
一开始想的是对于每个 ,用树状数组求
有多少个,属实智障。
再想想,当某个 满足时,
一定也都满足,我们找到最大的
就可以了。然后
参与的区间个数就是
了。
然后对于每个 ,其所对应的最大
一定是单调不减的。我们从
到
扫一遍即可。
复杂度是
还有个细节, 要开
!!!居然因为这个
了好一会儿。。
代码如下
#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;
}
京公网安备 11010502036488号