题目大意

给定长为 的序列,求其所有长度 的子区间的第 大数,所构成多重集的第 大数。

题解

二分。

现判定 。问题转化为求第 大数 的子区间个数,若其 则判定失败,否则判定成功。

如果将原始序列转化为一个 01 序列,某位置上为 表示原始序列上这个位置的数 ,否则为 ,那么区间个数就等于为该 01 序列中子区间和 的区间个数。这个显然是可以用尺取法计算的。

#include <bits/stdc++.h>
#define INF 2000000000
using namespace std;
typedef long long ll;
int read(){
    int f = 1, x = 0;
    char c = getchar();
    while(c < '0' || c > '9'){if(c == '-') f = -f; c = getchar();}
    while(c >= '0' && c <= '9')x = x * 10 + c - '0', c = getchar();
    return f * x; 
}
int n, k;
ll m;
int a[100005], b[100005];
void init(){
    n = read(), k = read();
    scanf("%lld", &m);
    for (int i = 1; i <= n; ++i)    
        a[i] = read(), b[i] = a[i];
    sort(b + 1, b + n + 1);
}
bool judge(int x){
    ll res = 0;
    for (int i = 1, cur = 0, cnt = 0; i <= n; ++i){
        while (cur < n && cnt < k){
            ++cur;
            if (a[cur] > x) ++cnt;
        }
        if (cur == n && cnt < k) break;
        res += (n - cur + 1);
        if (a[i] > x) --cnt;
    }
    return res < m;
}
void solve(){
    int l = 1, r = n;
    while (r > l){
        int mid = (l + r) >> 1;
        if (judge(b[mid]))
            r = mid;
        else l = mid + 1;
    }
    printf("%d\n", b[l]);
}
int main(){
    int T = read();
    while (T--){
        init();
        solve();
    }
    return 0;
}

小结

有的时候二分判定的不仅仅是一个答案,还可以是一个答案区间。

这个大部分时候都是显然的,但是有的时候会忽略。