hdu 6231

(二分+尺取法)
题意:给定长度为 n(n<105) 的数组 a ,然后拿出这个数组里所有长度大于 k(kn) 的区间,并将这些区间中的第 k 大元素拿出来放到另一个数组里,求新生成数组的第 M 大元素是多少。

思路:这题入手点很关键,由于答案肯定是 a 数组元素之一,而 a 中元素越大最后排名肯定越靠前,所以可以试试二分答案,然后接下来应该想到如何确定这个元素在新数组的排名,于是用尺取法统计出有多少个区间的第 k 大元素比它大,便可以求解。

注意: M 可能超过int,并且二分要写对~

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#define LL long long

using namespace std;
const int maxn = 100010;

int a[maxn], b[maxn];

LL get_rk(int x, int n, int k) {
    int r = -1;
    LL ret = 0, rk = 0;
    for(int l=0; l<n; l++) {
        while(rk < k && r < n) {
            rk += a[++r]>=x? 1:0;
        }
        if(rk >= k)
            ret += (LL)(n-r);
        rk -= a[l]>=x? 1:0;
    }
    //printf("x:%d ret:%I64d\n",x,ret);
    return ret;
}

void solve() {
    int n, k; LL m;
    scanf("%d%d%I64d",&n,&k,&m);
    for(int i=0; i<n; i++) {
        scanf("%d",&a[i]);
        b[i] = a[i];
    }
    sort(b, b+n);
    int l = 0, r = unique(b, b+n) - b - 1;
    while(l < r) {
        int mid = (l+r+1) / 2;
        if(get_rk(b[mid], n, k) >= m) l = mid;
        else r = mid - 1;
        //printf("l:%d r:%d\n",l,r);
    }
    printf("%d\n",b[l]);
}

int main() {
    int T;
    scanf("%d",&T);
    while(T --) {
        solve();
    }
    return 0;
}
发布了40 篇原创文章 · 获赞 44 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/Site1997/article/details/78926694