1686 第K大区间
定义一个区间的值为其众数出现的次数。
现给出n个数,求将所有区间的值排序后,第K大的值为多少。
输入
第一行两个数n和k(1<=n<=100000,k<=n*(n-1)/2)
第二行n个数,0<=每个数<2^31
输出
一个数表示答案。
输入样例
4 2
1 2 3 2
输出样例
2
思路:
两个性质:1、序列的长度是 n ,所以序列可能的最大值也就是 n 。
2、对于一个序列的值如果是 num ,那么:必定存在序列的值是 num-1 、num-2、num-3 …… 1 。
设 fun( num ) 是 ” 值大于 num 的序列 “ 的数量,那么我们就有:
当 fun( num ) == k , 那么最终结果就是 num+1 ;
当 fun( num ) > k 时,最终结果就应该大于 num ;
当 fun( num ) < k 时,最终结果就应该小于或等于 num 。
所以可以对 num 在 1~n 范围内进行二分答案,每次可以用尺取可以在 O(n) 计算出 fun( num ).
#include<bits/stdc++.h>
using namespace std;
typedef long long Lint;
const int max_n = 1e5+1e2;
int a[max_n*2];
vector<int> v;
int n;
Lint k;
int flag[max_n*2];
/*
假设我们求出区间 <l,r> 的值大于 num ,那么区间 < L , R > (L=1~l, R=r~n) 的值也大于num
*/
Lint fun(int num){ //此函数求出是值大于num的区间
Lint res=0;
memset(flag,0,sizeof(flag));
int l=1,r=1;
flag[a[1]]=1;
while(true){
while(r<=n && flag[a[r]]<=num){
r++;
flag[a[r]]++;
}
if(r>n) break;
Lint temp=l;
while(flag[a[r]]>num&&l<=r){
flag[a[l]]--;
l++;
}
temp=(Lint)l-temp;
res+=temp*(Lint)(n-r+1); //每次结果加上 r后面数字的个数*l移动的距离
}
return res;
}
void Init(){ //数字较大所以离散化一下
scanf("%d %lld",&n,&k);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
v.push_back(a[i]);
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
for(int i=1;i<=n;i++){
a[i]=lower_bound(v.begin(),v.end(),a[i])-v.begin()+1;
}
}
void solve(){
Init();
int l=1,r=n,mid;
while(r-l>1){
mid=(l+r)/2;
Lint temp=fun(mid);
if(temp==k){
printf("%d\n",mid+1);
return ;
}else if(temp>k){
l=mid;
}else if(temp<k){
r=mid;
}
}
if(fun(l)>=k){
printf("%d\n",r);
}else{
printf("%d\n",l);
}
}
int main(){
solve();
return 0;
}