题目描述
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
算法/思路分析
首先,本题直接记录数组ans再排序找中位数,复杂度O(n2)过高,会TLE。考虑优化,首先对cat进行顺序排序,则可去掉绝对值计算,中位数可根据计算出的ans在数组中的名次判定,名次计算则根据满足cat[j]-cat[i] <= p(j > i)的个数算出p在ans数组中的名次,即cat[j] <= cat[i] + p,循环i,判定满足条件的j的个数。下标j的计算可使用二分优化,记录cat[j] <= cat[i] + p时cat[j]在cat数组中的最终位置final,则从i+1到final都是符合题意的。
注:由于数据较大,cin会TLE,故应使用scanf()获取输入数据
代码
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <vector>
using namespace std;
const int maxn = 1e5 + 10;
int cat[maxn];
int n;
int rank1(int p)
{
int k = 0;
for(int i = 1; i <= n;i++)
{
int s = cat[i] + p;
int l = i + 1, r = n, tt = -1;
while(l <= r)
{
int mid = (l + r) >> 1;
if(cat[mid] <= s)
{
tt = mid;
l = mid + 1;
}
else r = mid - 1;
}
if(tt == -1) continue;
else k += tt - i;
}
return k;
}
int main()
{
while(~scanf("%d",&n))
{
memset(cat,0,sizeof(cat));
for(int i = 1; i <= n; i++)
scanf("%d",&cat[i]);
sort(cat + 1,cat + n + 1);
int pp = (n*(n-1)/2+1)/2; //中位数位置
int l = 0, r = cat[n] - cat[1];
int re = 0;
while(l <= r)
{
int mid = (l + r) >> 1;
if(rank1(mid) < pp) l = mid + 1;
else
{
re = mid;
r = mid - 1;
}
}
printf("%d\n",re);
}
return 0;
}