B - K-th Number HDU - 6231[二分+尺取]

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/m0_38081836/article/details/83419411

题意:给出n,m,k有n个数字,询问这个数组中所有区间第k大数字组成的数组中第m大的数字。(好绕啊,希望能懂OAO)


题解:尝试考虑一下我们如果已经知道一个数字之后,在数组中大于有m以及多余m个区间的第k大大于这个数字,那么这个数字一定不可能是答案,根据这个我们可以确定了一个单调的性质,所以我们可以二分答案。如何去检查有多少给个区间的第k大的数字大于当前数字的区间有多少个,这时候需要用一个尺取的方法来维护,我们在维护的过程中如果区间个数超过了m那么当前的答案一定是偏小的,否则则是偏大的。
尺取如何工作:我们尺取区间的时候,如果某个区间的第k大大于当前的数字时候,我们之后加上后面的所有区间(有点含糊)的时候只会使得当前的第k大变为第(k+1)大或者(k+2)大等等。这样就能o(n)的维护出有多少个区间的第k大大于当前数字,从而确定当前数字是否合法。


收获:问题的模型有时候需要转化一下,并不能生硬的去解题,如第m大可以转化为有(m-1)个比m大的数字,二分思想可以降低超多复杂度。


a c   c o d e ac\ code

/**hqx is the best**/
/*****数组大小*****/
/**hqx is the best**/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define debug cerr << "*" << endl;
#define rep(i, a, b) for(int i = a; i <= b; i++)
#define pre(i, a, b) for(int i = a; i >= b; i--)
#define met(a, b) memset(a, b, sizeof(a))
const int maxn = 1e6 + 10;
const int inf = 0x3f3f3f3f;
ll T, n, k, m, a[maxn], b[maxn];

bool check(ll mid) {
    ll l = 1, r = 0, num = 0;
    ll ans = 0;
    while(r <= n) {
        if(num < k) {
            if(a[r + 1] >= mid) num++;
            r++;
        } else {
            if(num == k) ans += (n - r + 1);
            if(a[l] >= mid) num--;
            l++;
            if(ans >= m) return true;
        }
    }
    return false;
}

int main() {
    scanf("%lld", &T);
    while(T--) {
        scanf("%lld%lld%lld", &n, &k, &m);
        rep(i, 1, n) {
            scanf("%lld", &a[i]);
        }
        memcpy(b, a, sizeof(a));
        ll l = 1, r = n, ans;
        sort(b + 1, b + 1 + n);
        while(l <= r) {
            ll mid = (l + r) >> 1;
            if(check(b[mid])) {
                ans = b[mid];
                l = mid + 1;
            } else {
                r = mid - 1;
            }
        }
        printf("%lld\n", ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/m0_38081836/article/details/83419411