2017中国大学生程序设计竞赛-哈尔滨站 (B.尺取 + 二分)

Problem Description
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

题意:给你一个长度为n的a序列,找出a序列的所有子序列第k大的数组成b序列(子序列长度大于等于k),然后求b序列的第m大的数。

解题思路:先用二分一个 x,用尺取法找出在a序列中第k大数大于等于 x 的子序列的个数 sum,若 sum >= m,二分向右移,若sum < m 向左移。若一个数 x 在 b 序列中第 m 大 ,说明 a 序列中有 m - 1 个子序列的第 k 大 数大于等于 x ,因为 b 序列后面 m - 1 个数是由 a 序列中的 m - 1 个子序列构成的。(单调性:若以 x 为子序列的 k 大的数的子序列越多,说明 x 值的越小)。

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long LL;

const int maxn = 1e5 + 5;
int n,k,a[maxn],b[maxn];
LL m;

bool check(int x){
    LL sum = 0;
    int num = 0,pos = 1;
    for(int i = 1;i <= n;i++){//用尺取求出区间第k大数大于等于x的这样的区间有多少个。
        if(a[i] >= x) num++;
        if(num == k){
            sum += n - i + 1;
            while(a[pos] < x){
                sum += n - i + 1;
                pos++;
            }
            num--,pos++;
        }
    }
    if(sum >= m) return true;//要注意同类型比较。
    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 num = unique(b + 1,b + n + 1) - (b + 1);//先排序后去重,返回的无重复序列的长度。
        int l = 1,r = num;
        while(l <= r){
            int mid = (l + r) >> 1;
            if(check(b[mid])) l = mid + 1;
            else r = mid - 1;
        }
        printf("%d\n",b[r]);
    }
}

你可能感兴趣的:(ACM,-,平常水题)