NC 14301 二分 + 尺取法

题意

传送门 NC 14301

题解

区间个数 O ( n 2 ) O(n^2) O(n2),答案的可能值为 A A A 中非前 K − 1 K-1 K1 大的数,按排序后索引二分。对于每一个二分值 m i d mid mid,统计 B B B 中比 A [ m i d ] A[mid] A[mid] 大的数,判断是否小于 M M M

统计 B B B 中比 A [ m i d ] A[mid] A[mid] 大的数,即求 A A A 中第 K K K 大的数大于 A [ m i d ] A[mid] A[mid] 的区间个数。那么对于 A [ i ] A[i] A[i]

A ′ [ i ] = { 1 A [ i ] > A [ m i d ] 0 o t h e r w i s e A'[i]=\begin{cases} 1& A[i]>A[mid]\\ 0& otherwise\\ \end{cases} A[i]={10A[i]>A[mid]otherwise

问题转化为求 A ′ A' A 中区间和大于等于 K K K 的个数。尺取法求解:找到一个右界 t t t 使区间和恰好等于 K K K,在保证区间和不变的情况下收缩左界 s s s,每次收缩则区间个数增加 N − t + 1 N-t+1 Nt+1,即以 s s s 为左边界的所有区间和大于等于 K K K 的区间个数;左界收缩至区间和恰好不为 K K K 时,继续上一步操作。

#include 
using namespace std;
#define inf 0x3f3f3f3f
#define maxn 100005
typedef long long ll;
ll N, K, M;
int A[maxn], B[maxn], C[maxn];

bool judge(int x)
{
    for (int i = 0; i < N; i++) C[i] = A[i] > x ? 1 : 0;
    int s = 0, t = 0, sum = 0;
    ll res = 0;
    for (;;)
    {
        while (t < N && sum < K)
        {
            sum += C[t++];
        }
        if (sum < K) break;
        while (sum == K)
        {
            res += N - t + 1;
            sum -= C[s++];
        }
    }
    return res < M;
}

int main()
{
    int t;
    scanf("%d", &t);
    while (t--)
    {
        scanf("%lld%lld%lld", &N, &K, &M);
        for (int i = 0; i < N; i++) scanf("%d", A + i);
        memcpy(B, A, sizeof(A));
        sort(B, B + N);
        int lb = -1, ub = N - K;
        while (ub - lb > 1)
        {
            int mid = (lb + ub) >> 1;
            if (judge(B[mid])) ub = mid;
            else lb = mid;
        }
        printf("%d\n", B[ub]);
    }
    return 0;
}

你可能感兴趣的:(二分,&,三分,常用技巧)