主要思路:
由于本题的n最大为1e5,所以仅仅求出ans数列已经达到了O(n2)的复杂度,所以需要其他的思路;
二分中位数
考虑我们求的中位数的范围:大于等于零,这是因为ans的每个元素为abs(cat[i] - cat[j]);小于等于cat数列最大值与最小值之差,这是因为ans的每个元素由cat生成。并且名次是单调的,所以在这个范围内我们可以二分求出中位数,使用的一个评判标准便是名次与(len(ans)+1)/2的大小关系
名次的计算
所以,现在的问题变成了求一个数P的名次,即求出在cat中有多少cati和catj满足|cati - catj| <=P 这样的数对。这里去掉绝对值的方法,可将cat数组提前排序就可以保证cati <= catj,进而满足的关系可变形为cati +P >= catj,于是,我们对于每一个cati 我们去二分找到符合条件的所有catj,进而求出名次
C - TT的神秘礼物
TT 是一位重度爱猫人士,每日沉溺于 B 站上的猫咪频道。
有一天,TT 的好友 ZJM 决定交给 TT 一个难题,如果 TT 能够解决这个难题,
ZJM 就会买一只可爱猫咪送给 TT。
任务内容是,给定一个 N 个数的数组 cat[i],并用这个数组生成一个新数组 ans[i]。
新数组定义为对于任意的 i, j 且 i != j,均有 ans[] = abs(cat[i] - cat[j]),1 <= i < j <= N。
试求出这个新数组的中位数,中位数即为排序之后 (len+1)/2 位置对应的数字,'/' 为下取整。
TT 非常想得到那只可爱的猫咪,你能帮帮他吗?
Input
多组输入,每次输入一个 N,表示有 N 个数,
之后输入一个长度为 N 的序列 cat, cat[i] <= 1e9 , 3 <= n <= 1e5
Output
输出新数组 ans 的中位数
Sample Input
4
1 3 2 4
3
1 10 2
Sample Output
1
8
A Possible Solution
#include<stdio.h>
#include<algorithm>
using namespace std;
int n,arr[100005];
int getRank(int target){
if(target==0)
return 0;
int res=0;
for(int i=0;i<n;i++){
int l=i+1,r=n-1,mid,ans=0;
while(l<=r){
mid=(l+r)>>1;
if(arr[mid]>arr[i]+target)r=mid-1;
else{
ans=mid;
l=mid+1;
}
}
if(ans)
res+=(ans-i);
}
return res;
}
int main(){
while(~scanf("%d",&n)){
for(int i=0;i<n;i++){
scanf("%d",arr+i);
}
sort(arr,arr+n);
// for(int i=0;i<n;i++)printf("%d ",arr[i]);
int l=0,r=arr[n-1]-arr[0],target=(2+n*(n-1))/4,rank_mid,mid,ans;
// for(int i=l;i<=r;i++){
// printf("%d ",getRank(i));
// }printf("\n");
while(l<r){
mid=(l+r)>>1;
rank_mid=getRank(mid);
if(rank_mid>=target)
r=mid;
else
l=mid+1;
/*if(rank_mid>target)r=mid-1;
else if(rank_mid<target)l=mid+1;
else{
ans=mid;
r=mid-1;
}*/
}
printf("%d\n",l);
}
return 0;
}