题目链接
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;
}
}
京公网安备 11010502036488号