HDU 6231 K-th Number CCPC2017 Harbin(二分答案)

来源:互联网 发布:手机淘宝卖东西怎么弄 编辑:程序博客网 时间:2024/06/06 12:43

K-th Number

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 17    Accepted Submission(s): 9

Problem Description

Alice are given an array A[1..N] with N numbers.
Now Alice want to build an array B by a parameter K as following rules:
Initially, the array B is empty. Consider each interval in array A. If the length of this interval is less thanK, then ignore this interval. Otherwise, find the K-th largest number in this interval and add this number into array B.
In fact Alice doesn't care each element in the array B. She only wants to know theM-th largest element in the array B. Please help her to find this number.

Input

The first line is the number of test cases.
For each test case, the first line contains three positive numbers N(1N105),K(1KN),M. The second line contains N numbers Ai(1Ai109).
It's guaranteed that M is not greater than the length of the array B.

Output

For each test case, output a single line containing the M-th largest element in the array B.

Sample Input

25 3 22 3 1 5 43 3 15 8 2

Sample Output

32

Source

2017中国大学生程序设计竞赛-哈尔滨站-重现赛(感谢哈理工)



        其实这题非常的不应该,非常非常不应该,因为自己做过类似的题目。17年湖南多校,大笨龙那题:传送门

        但是都说了赛场上有毒,没怎么想,而且还想偏了,去想HDU多校赛那题了……然后就没有然后了……

        回顾一下这个思想,关于第K大的判定问题,我们通常可以用二分答案加上模糊化处理。即,我们二分枚举最后的答案,然后把所有的数字模糊化处理,就是把数字分为大于等于答案的和小于答案的。大于的表示为1,小于的表示为0。问题模糊化之后就可省去很多时间了。

        比如说这题的,把所有的区间第K大放到一个新的数组中,然后排序,输出第M大。我二分枚举这个最后的第M大,假设为X,那么我们需要只考虑大于等于X的数字。于是相当于我们只需要判断在这个新数组中,大于等于X的数字的数量是否大于M,也即第K大大于等于X的区间的数量是否大于M。最后变成了统计第K大大于X的区间。模糊化处理之后,我们发现,如果用0、1替代,我们就能通过区间和来判定该区间的第K大是否大于等于X。如果区间和大于等于K,那么第K大就大于等于X。统计数量也相对简单了很多。我们枚举左端点,然后找右端点,知道找到一个点恰好区间和为K,那么以这个点以及这个点之后的所有点为右端点的区间第K大肯定大于等于X。这样子做看似还是O(N^2)的,但是我们发现,当左端点右移一格之后,这个右端点的最小值是不下降的,也就是说可以用类似离线处理的方法,做到用O(N)的复杂度统计。于是此题就迎刃而解了,需要注意的是,这个M可能比较大,需要用LL,然后统计区间的时候也要用LL。具体见代码:

#include <bits/stdc++.h>#define LL long long#define N 100010using namespace std;int n,k,tot,a[N],b[N],s[N];map<int,bool> mp; LL m;bool check(int x){    int p=1; LL res=0;    for(int i=1;i<=n;i++)        s[i]=s[i-1]+(a[i]>=x);//模糊化处理,同时计算前缀和    for(int i=1;i<=n;i++)    {        while(s[p]-s[i-1]<k&&p<n) p++;//找到使得区间和大于K的第一个点,即对应右端点的最小值        if (s[p]-s[i-1]!=k) break; res+=n-p+1;//之后的所有点都可以当作右端点        if (res>=m) return 1;    }    return res>=m;//判断区间数与M的关系}bool cmp(int a,int b){    return a>b;}int main(){    int T_T;    cin>>T_T;    while(T_T--)    {        mp.clear(); tot=0;        scanf("%d%d%I64d",&n,&k,&m);        for(int i=1;i<=n;i++)        {            scanf("%d",&a[i]);            if (!mp[a[i]])            {                mp[a[i]]=1;                b[++tot]=a[i];            }        }        sort(b+1,b+1+tot,cmp);        int l=1,r=tot,mid,ans;        while(l<=r)        {            mid=(l+r)>>1;            if (check(b[mid])) ans=b[mid],r=mid-1;                        else l=mid+1;        }        printf("%d\n",ans);    }}