HDU - 6231 K-th Number 二分 + 尺取法(好题)

1.题意:给你n个数字的序列,让你把任意一个连续区间内第k大的数字插入到数组B里面,最后求B中第m大的数字。

2.分析:

(1)比赛到时候致命的一个错误就是:把第k大的数字理解反了。。2 3 1第三大 , 自以为是3了(应该是1)。。然后按照错的题意 意*了各种做法。。而且开场第一题我给读漏了条件,唉。。读题太差了。。昨天那场直接自闭了。

注:对于题意还有一坑:所有第k大的数字都要插入B,不论是否重复,比如2 2 3 3 3第二大的数字就是 3 而不是2 。。

(2)理清题意以后,二分的思路还是很难想的,这种题二分了怎么验证也是这个题的难点,你得想到二分还得想到怎么验证。(好了不扯犊子了)

(3)对于二分思想:

假设我们设定x为B中第m大的数字,也就是说B中比x大的还有(m - 1)个,这(m-1)个数字作为某个区间第k大的数字被插入了(m-1)次:

<1>若大于 x 的数字y(y>x) 作为区间第k大的数字 出现了 n(n> m - 1)次,也就是往B里面插入了>=m次 , 这时B中第m大的数字肯定 > x , 所以这时候真实答案应该大于x。

<2>若若大于 x 的数字y(y>x) 作为区间第k大的数字 出现了 n(n<= m - 1)次,也就是往B里面插入了<=m-1次,这时B中第m大的数字肯定 <= x , 所以可能存在比x还小的数字,我们应该再取 比x小的数字验证。

(4)验证思想(尺取法/滑动窗口):怎么寻找大于等于X的数字作为区间第k大的数字的区间个数有多少个呢?

我们动态维护一个含有比x大的数字有k个的区间,若此区间左右边界为[ l , r ],则这样的区间还有(n - r)个,左侧区间已经满足了临界,右侧区间情况都要加上。然后左端点不断右移,维护这样的区间,求其个数判断与m-1的关系即可

3.隐藏坑点:m要开long long ,题目没指明范围。。

4.代码:

#include <iostream>
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 100000 + 100;
LL num[maxn],order[maxn];
int n,k;
LL m;
bool judge(LL p){//尺取验证
   LL sum = 0;//记录区间和
   int cnt = 0;//记录在这个区间内大于p的数字个数
   int r = -1;//右端点
   int l = 0;//左端点
   while(r<n){
      if(cnt<k){//先找齐大于p的k个数字在这个区间内为临界
         if(r+1<n&&num[r+1]>p)cnt++;//用r+1的原因是保持是在[ l, r]内有k个大于p的数字,若用r,
         r++;//则有k个大于p的数字所在区间为[ l, r-1 ]
      }
      else{//求这个区间所有数目
        if(cnt==k)sum+=(n - r);//临界区间之后的
        if(sum>m-1)return false;
        if(num[l]>p)cnt--;//右移左端点
        l++;
      }
   }
   return true;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d%lld",&n,&k,&m);
        for(int i = 0;i<n;i++){
            scanf("%lld",&num[i]);
            order[i] = num[i];
        }
        sort(order,order + n);
        int l = 0,r = n-1;
        LL ans = order[0];
        while(l<=r){//二分答案
            int mid = (l+r)>>1;
            if(judge(order[mid])){//验证<=m-1,则可能有更小的
                //cout<<order[mid]<<endl;
                ans = order[mid];
                r = mid-1;
            }
            else l = mid+1;//否则只能比当前大
        }
        printf("%lld\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40772692/article/details/82664630