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

题目链接:

题意:

给出一个长为N的序列,求出所有子序列中的第k大数(子序列长度必然大于等于k)并将其添加到一个新的序列中,求这个新序列的第m大为多少。

思路:

(本弱鸡打这场模拟赛时没有做出来,太惨了...)

首先,看榜过的人不是很多,加上这种 求第k大的数(肯定不会用到 主席数,树状数组),因为求的是所有子区间,加上又有多组样例。所以我就一直想子区间之间的关系,一开始想既然是第 k 大,那么长度为 k的子区间,就是求其区间最小值(单调队列)。然后再 希望通过某种 递推关系 去一次推出区间长度+1 情况的第k大值。但是没有想出来这种思路的解法(可能方向都错了)。

模拟赛打完后看了题解,觉得的确非常巧妙。题解是通过找到了 某个值 与第k大,以及与新形成序列 的m大 之间的关系,求得的。

比如 对于下面样例序列(答案为3)

5 3 2
2 3 1 5 4

首先我们知道最后新的序列组成 来源于原序列。我们对原序列排序 1 2 3 4 5 , 那么新序列的组成形式为 ( 1(a) , 2(b), 3(c) ,4 (d), 5(e) )//这里的a,b,c,d,e表示出现次数

若原序列中,所有长度不小于k的连续子序列中,第k大数不小于x,的子序列一共有ans个,那么x在所有第k大元素组成的数列中的位置排在 ans 个之后了

而对于 求解 ans个做法即时,首先找到一个区间从 l 开始第一个满足 x 为第 k 大的 r 位置(尺取法),然后对于其右边的长度部分 (n-r+1) , 如果有比 x 小,则对 x 排名没影响;

比x大,x排名就降低(就统计排在第k大,并且大于x的区间数)。所以这样就求出了 所有第k大数不小于x的子序列个数(子区间)。由于尺取是 O(N)

我们如果再遍历搜索就会到 O(N^2) , 所以我们再使用二分搜索 降到 O(N logN) 复杂度。

code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+7;
const int inf = 0x3f3f3f3f;

ll a[maxn];
ll b[maxn];
ll n,k,m;

bool judge(int x){
    int num=0,j=1;
    ll ans =0;
    int l=1,r=0;
//    尺取法的两种写法
    while(r<=n){
        if(num<k){
            if(a[r+1]>=x) num++;
            r++;
        }else{
            if(num==k) ans += n-r+1;
            if(a[l]>=x) num--;
            l++;
        }
    }
//    for(int i=1;i<=n;i++){
//        if(a[i]>=x) num++;
//        if(num==k){
//            ans += n-i+1;
//            while(a[j]<x){
//                ans += n -i +1;
//                j++;
//            }
//            num--;
//            j++;
//        }
//    }
    //小于或者等于
    if(ans>=m) return true;
    //大于
    else return false;
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%lld %lld %lld",&n,&k,&m);
        memset(b,0,sizeof(b));
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            b[i] = a[i];
        }
        sort(b+1,b+1+n);
        int size = unique(b+1,b+1+n) - (b+1); 
        int l =1,r= size;
        while(l<=r){
            int mid = (l+r)>>1;
            if(judge(b[mid])){
                   l = mid+1;
            }else{
                r = mid-1;
            }
        }
        printf("%d\n",b[l-1]);    
    }
    return 0;
}

 

posted @ 2019-09-12 19:04  Tianwell  阅读(213)  评论(0编辑  收藏  举报