K-th Number HDU - 6231(二分+尺取)

K-th Number HDU - 6231

Alice are given an array A[1…N] with N numbers.

Now Alice want to build an array B by a parameter K as following rules:

Initially, the array B is empty. Consider each interval in array A. If the length of this interval is less than K, then ignore this interval. Otherwise, find the K-th largest number in this interval and add this number into array B.

In fact Alice doesn’t care each element in the array B. She only wants to know the M-th largest element in the array B
. Please help her to find this number.
Input
The first line is the number of test cases.

For each test case, the first line contains three positive numbers N(1≤N≤105),K(1≤K≤N),M. The second line contains N numbers Ai(1≤Ai≤109)
.

It’s guaranteed that M is not greater than the length of the array B.
Output
For each test case, output a single line containing the M-th largest element in the array B
.
Sample Input

2
5 3 2
2 3 1 5 4
3 3 1
5 8 2

Sample Output

3
2

题意:

给你数列A,对于A的每一个区间,求第K大,插入到数列B中,最后再求数列B的第M大!

分析:

首先二分答案,每次二分的时候我们得到一个x,这个时候我们利用尺取,去得到第K大数大于等于x的区间一共有多少个,具体操作如下

我们从头开始记录a[i] >= x的个数,当恰好有k个数大于等于x了,这个时候假如说就是到下标i的时候恰好有k个数大于等于x了,那么区间[1,i]内一定能够作为一个区间,找到一个第K大数大于等于x,那么同理我保持前面不动,向后一个一个的扩展,也一定能找到第K大的数大于等于x,即在区间[1,i+1],[1,i+2],[1,i+3]…[i,n],所以这些区间都可以找到第K大的数大于等于x,而后面这些区间的个数为n-i,再加上[1,i]这个区间,那么此时共有n-i+1个区间其第K大的数大于等于x。
K-th Number HDU - 6231(二分+尺取)_第1张图片
那么只有这些吗,我们定义下标j,让j从头开始,即从1开始,往后移动,如果a[j] < x,那么这个数a[j]对于能否取到第K大的数大于等于x没有影响,因为它比x小吗,取肯定不会取它,所以这个时候我们可以去掉它,以下一个为开始,这样相当于起点变了,但是终点还是i呀,所以所有可以的区间还是[j,i],[j,i+1],[j,i+1]…[j,n],而且区间[j,i]中仍然后k个大于等于x的数,没有变,所以我们区间个数还是加上n-i+1,这个过程while循环即可,知道a[j] >= x停止,否则可以一直进行
K-th Number HDU - 6231(二分+尺取)_第2张图片

如果这个时候a[j]是一个大于等于x的数,即a[j] >= x,此时我们停止循环,因为我们要减掉这个数,从下一个开始,因此循环停止后,j++,表示从下一个开始,而且大于等于x的个数要减1,这个时候,我们就得变化终点i了,i往后寻找当找到又一个大于等于x的时候,个数加1,如果总个数又等于K个了,那么重复上面的操作就行了。

这样我们就可以求得第K大数是大于等于x的区间一共有多少个,如果大于等于M个说明我们枚举的x数小了,有太多第k大大于等于x了,因此二分答案保留右区间,否则保留左区间,知道最后,x就是答案。

code:

#include 
using namespace std;
typedef long long ll;
int N,K;
ll M;
int a[100005];

bool judge(int x){
    ll ans = 0;
    int num = 0;
    int j = 1;
    for(int i = 1; i <= N; i++){
        if(a[i] >= x)
            num++;
        if(num == K){
            ans += N - i + 1;
            while(a[j] < x){
                ans += N - i + 1;
                j++;
            }
            num--;
            j++;
        }
    }
    if(ans >= 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]);
        }
        int l = 1,r = 1000000000;
        int m;
        while(l < r){
            m = l + r >> 1;
            if(judge(m))
                l = m + 1;
            else
                r = m;
        }
        printf("%d\n",l-1);
    }
    return 0;
}

你可能感兴趣的:(思维技巧,计算方法)