前言:第M大元素,依然可以用二分+验证解决。现在问题是如何验证。记二分枚举元素位x,则应找到>=x的元素大于k的区间个数。因为这种区间的第k大的数在数组b中一定排在x的前面或一块(相等)。如果区间个数>=m,说明枚举的x<=第m大元素,反之,x>第m大元素。 注意:二分查找要求元素有序,但是排序过后的数组元素的位置就变了。比如1,4,5,1,2,3,排序完变成112345,第一到第三个元素从145变成112,与原数组明显不同,所以要用一个数组存排序过后的来二分,一个存原始数组 思路&AC代码:

#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
int n,k;
long long m;
int a[100010];
int b[100010];
bool judge(int x)
{
    ll cnt=0;
    ll res=0;
    for(int i=1,j=1;j<=n;++j)//滑动窗口,i与j都接近末尾时,cnt不会>=k,所以i不会++使得数组越界,而++j,会使得循环终止
    {
        if(a[j]>=x) {++cnt;}//右指针右移找>=x的元素
        while(cnt>=k)//一旦当前的区间符合要求
        {
            res+=n-j+1;//植树原理,那么当前做指针到右指针及右指针以后的所有区间都符合cnt>=k,一定会有第k大的数>=x
          //下面两行左指针右移,如果左指针右移能使得cnt,就要继续找>=x的元素
            if(a[i]>=x) {--cnt;}
            ++i;
        }
    }
    return res>=m;//有m个数>=当前二分枚举的元素,1.取小了2.答案就是他(因为a中可能存在相等的元素)。若>=当前二分的元素小于m,一定是取大了,因为前面已经占了m个比它大的数了
}
int main()
{
    ios::sync_with_stdio(0);cin.tie(0);
    int t;cin>>t;
    while(t--)
    {
        cin>>n>>k>>m;
        for(int i=1;i<=n;++i) {cin>>a[i];b[i]=a[i];}
        sort(b+1,b+1+n);
        int l=1,r=n,mid;
        while(l<r)
        {
            mid=(l+r+1)>>1;//相当于找<=要求的最后一个元素,因为总存在符合要求的元素,根据毕竟思想,退出循环是,l就是该元素的下标。也可以改代码找>=要求的第一个元素
            if(judge(b[mid])) {l=mid;}
            else {r=mid-1;}
        }
        cout<<b[l]<<'\n';
    }
    return 0;
}

/随手一记:目前的题解中还没写过实数域二分及三分(还有两个凸凹函数三分套三分求整体极值:传送带最小覆盖圆)的题(通过r-l>eps(很小值)或者限制循环次数来结束循环),通过引入极小误差eps来比较大小。double mid1=l+(r-l)/3,mid2=r-(r-l)/3 (区间长度的1/3)。还有通过模拟退火和粒子群算法来求多峰函数极值??/