zoukankan      html  css  js  c++  java
  • 【每日一题】20.K-th Number (二分 + 尺取)

    关于此题,我们分析一下:
    一个区间第k大的数不小于x的条件是什么?

    答案就是一个区间内不小于x的数的个数不小于k

    那么,我们就会发现,我们其实并不需要知道每个数的值,实际上对我们有用的只有每个数与x的大小关系,然后,我们就可以直接用贡献法计算。

    我们把所有值不下于x的赋为1,剩下的赋为0,那么,二分求的东西就被转换成了:

    有多少个区间的区间和不下于k,且序列里面的值只可能是0或1

    然后随便搞个前缀和加个单调指针就行了

    (其实可以不用单调指针直接用桶存的,常数更小,但是,由于脑袋有点晕,出锅了,就懒得改了)

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    #define int long long
    const int N = 1e5 + 1;
    int a[N], b[N];
    int n, k, m;
    inline int calc(int x) {
        for (int i = 1; i <= n; ++i) {
            b[i] = (a[i] >= x);
            b[i] += b[i - 1];
        }
        int ans = 0, l = 1;
        for (int i = 1; i <= n; ++i) { //枚举右端点
            while (b[i] - b[l - 1] >= k) ++l;
            ans += (l - 1);
        }
        return ans;
    }
    inline int read() {
        char ch = getchar();
        int w = 0, ss = 0;
        while (ch < '0' || ch > '9') w |= (ch == '-'), ch = getchar();
        while (ch >= '0' && ch <= '9') ss = (ss * 10 + (ch - '0')), ch = getchar();
        return w ? -ss : ss;
    }
    signed main() {
        int T = read();
        while (T--) {
            n = read(), k = read(), m = read();
            for (int i = 1; i <= n; ++i) a[i] = read();
    
            int l = 1, r = 1e9, ans = 0;
            while (l <= r) {
                int mid = (l + r) >> 1;
                int tot = calc(mid); //区间kth大于等于mid的有几个
                if (tot >= m) {
                    ans = mid;
                    l   = mid + 1;
                } else
                    r = mid - 1;
            }
            printf("%lld
    ", ans);
        }
        return 0;
    }
    

    另外一种写法

    #include <cstdio>
    #include <iostream>
    using namespace std;
    const int N = 1e5;
    int a[N + 2], s[N + 2];
    int main() {
        int T, n, i, j, K, l, r, mid;
        long long m, res;
        scanf("%d", &T);
        while (T--) {
            scanf("%d%d%lld", &n, &K, &m);
            for (i = r = 1, l = 1e9; i <= n; i++) {
                scanf("%d", &a[i]);
                l = min(l, a[i]);
                r = max(r, a[i]);
            }
            while (l < r) {
                mid = l + r >> 1;
                for (i = 1, res = j = 0; i <= n; i++) {
                    s[i] = s[i - 1] + (a[i] > mid);
                    while (j < i && s[j] <= s[i] - K) j++;
                    res += j;
                }
                res >= m ? l = mid + 1 : r = mid;
            }
            printf("%d
    ", l);
        }
        return 0;
    }
    

    The desire of his soul is the prophecy of his fate
    你灵魂的欲望,是你命运的先知。

  • 相关阅读:
    08:特殊日历计算
    07:玛雅历
    Vigenère密码
    openjudge 螺旋加密
    C#遍历指定文件夹中的所有文件和子文件夹
    确定两串乱序同构
    方阵原地顺时针旋转90度
    05:统计单词数【NOIP2011复赛普及组第二题】
    牛客网一道趣味题
    二分查找的平均查找长度详解【转】
  • 原文地址:https://www.cnblogs.com/RioTian/p/14743737.html
Copyright © 2011-2022 走看看