zoukankan      html  css  js  c++  java
  • HDU

    题目链接:

    题意:

    给出一个长为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
    ",b[l-1]);    
        }
        return 0;
    }
  • 相关阅读:
    《VC++深入详解》学习笔记 第十二章 文件和注册表操作
    《VC++深入详解》学习笔记 第九章 定制应用程序外观
    《VC++深入详解》学习笔记 第七、八章对话框
    《VC++深入详解》学习笔记 第六章 菜单
    《VC++深入详解》学习笔记 第五章 文本编程
    《VC++深入详解》学习笔记 第四章 简单绘图
    《VC++深入详解》学习笔记 第三章 MFC框架程序剖析
    Inno_Setup使用笔记(简单完成安装包制作)
    《VC++深入详解》学习笔记 第一章 Windows程序内部运行机制
    搭建eclipse的nodejs开发环境图解
  • 原文地址:https://www.cnblogs.com/Tianwell/p/11514693.html
Copyright © 2011-2022 走看看