一道二分题目,题目难在不容易看出二分,看出二分难度基本就没了。。。(我不会)

  考虑可以被二分的原因:对于a数组中第k大的数比t大的子串的个数是随着t的减少而严格增大的,所以可以二分t,去寻找答案。

  对于如何查询确定t时,符合条件的子串数量:定义一个右端点和一个左端点,枚举右端点,求每次左端点的最大值,让其包含至少k个大于t的数即可。

  我的思路:我以为是要算每个数被计算的可能性,去求贡献,想了很久都做不出来,感觉二分的方法还是比较独特的,你不能根据每种情况的影响因素去分析如何优化,而是本身题目答案的单调性确保你可以使用二分计算出答案,而和这道题的题目性质本身没有什么关系。如果对二分不是很熟悉的人应该都看不出二分吧。。。因为正常人不会对这种题观察答案的单调性吧。。。

贴一下AC代码

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <string>
#include <iostream>
#include <queue>
using namespace std;
const int maxn = 1e5+10;
typedef long long ll;
ll a[maxn],k,n,m;
ll J(int t)
{
    ll ans=0,num=0;
    for(int i=0,j=-1;i<n;i++)
    {
        if(a[i]>=t)
            num++;
        while(i-j>k&&num>=k)
        {
            if(a[j+1]<t)
            {
                j++;
            }
            else
            {
                if(num==k)
                {
                    break;
                }
                else
                {
                    num--;
                    j++;
                }
            }
        }
        if(num>=k)
            ans+=j+2;
    }
    t;
    return ans;
}
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        cin>>n>>k>>m;
        for(int i=0;i<n;i++)
            cin>>a[i];
        int l=0,r=1e9+7,mid=0;
        J(3);
        while(r-1>l)
        {
            mid=(l+r)/2;
            if(J(mid)>=m)
            {
                l=mid;
            }
            else
                r=mid;
        }
        if(J(r)>=m)
        {
            cout<<r<<endl;

        }
        else
            cout<<l<<endl;
    }
}
02-01 16:01