题目链接

https://vjudge.net/problem/HihoCoder-1483

解题思路

二分第k小的值,假设为m,统计区间价值小于等于m的区间数量(这个统计的方法真是绝了),若数量比k大说明二分的区间右端点大了;反之,说明二分的区间左端点小了。

AC代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2e5+100;
ll k,a[N],b[N],n,vis[N];
int T;
bool check(ll x){//统计区间价值小于等于x的区间数量,太牛逼了!!!! 

    ll sum=0,num=0;
    memset(vis,0,sizeof vis);
    for(int l=1,r=1;l<=n;l++){
        for(;r<=n && sum+vis[b[r]]<=x;r++){
            sum+=vis[b[r]];//从区间右侧添加一个数字对整个区间价值的影响。在上一个区间的价值基础上加上与新添加数字相同的数字个数 
            vis[b[r]]++;//新添加的数字数量加1 
        }
        num+=r-l;//此时r指向sum<=x区间右端点的下一个 
        vis[b[l]]--;//左侧删除数字,类似于右侧添加新数字。注意先--,再统计。 
        sum-=vis[b[l]];
        if(num>=k) return 1;
    }
    return 0;

        //也可
    /*
    ll sum=0,num=0;
    memset(vis,0,sizeof vis);
    for(int i=1,j=1;i<=n;i++){
        sum+=vis[b[i]];
        vis[b[i]]++;
        while(j<=n && sum>x){
            vis[b[j]]--;
            sum-=vis[b[j]];
            j++;
        }
        num+=i-j+1;
        if(num>=k) return 1;
    }
    return 0;
    */
}
int main(){
    cin>>T;
    while(T--){
        memset(b,0,sizeof b);
        memset(a,0,sizeof a);
        cin>>n>>k;
        for(int i=1;i<=n;i++) cin>>a[i],b[i]=a[i];
        //离散化
        sort(a+1,a+n+1);
        int cnt=unique(a+1,a+n+1)-a-1;
        for(int i=1;i<=n;i++)
        b[i]=lower_bound(a+1,a+cnt+1,b[i])-a; 

        ll L=0,R=n*(n-1)/2;//左右端点注意 
        while(L<=R){
            ll mid=(L+R)>>1;
            if(check(mid))R=mid-1;
            else L=mid+1; 
        }
        cout<<R+1<<endl;
    }
}