目录


题目描述:

在你的养牛场,所有的奶牛都养在一排呈直线的牛栏中。一共有 \(n\) 头奶牛,其中第 头牛在直线上所处的位置可以用一个整数坐标 \(p_i(0\le p_i \le 10^8)\) 来表示。在无聊的日子里,奶牛们常常在自己的牛栏里与其它奶牛交流一些八卦新闻。每头奶牛发出的声音响度是一样的,而由于声波的能量衰减,某头奶牛发出的声音只能被与它距离不超过 \(d(0 \le d \le 10^4)\)的奶牛所听到,这样这对奶牛就称为可以相互交流的。现在给出所有奶牛的位置和声音所能传播的最远距离 \(d\) ,请你编个程序来计算你的养牛场里究竟有多少对可以相互交流的奶牛。

输入格式

第一行包含两个整数 \(n,d\)。

第二行包含 个整数,每个整数都是一个坐标 p_i,描述一头奶牛在直线上的位置。

输出格式

一个数,表示养牛场中可以相互交流奶牛的对数。

输入输出样例

输入 #1

5 10 10 12 16 37 40 

输出 #1

4 

说明/提示

数据规模

对于 \(40\%\)的数据,\(1 \leq n \leq 10^3\)。

对于 \(100\%\) 的数据,\(1 \leq n \leq 10^6\)。

题目讲解

1.给出的样例虽然是有序的,但是题目并没有点明,需要先排序再才能处理。这里的排序很朴素,用​​STL​​就行了,没必要手写。

2.(Key):有序后在怎么求对数呢?明显当数组中,\(abs(a[i]-a[j])\le d\),说明\(a[i]\)与\(a[j]\)成对。由此可以写出,朴素代码:

for (int i=1;i<=n;i++)
for (int j=i+1;j<=n;j++)
if (a[j]-a[i]<=d) ans++;

但是,时间复杂度高达\(O(n^2)\)。如何进行改进?

因为数组是有序的,我们可以用二分的方法找到数组小于等于\(a[i]+d\)的最大数,假设其坐标是\(j\),那么与\(a[i]\)成对的数,就有\(j-i\)(因为\(a[i]\)不能算,故不是\(j-i+1\))。核心代码为:

int search(int key) {
int l=1,r=n;
while (l<r) {
int mid = (l+r+1)/2;
if (p[mid]<=key) l = mid;
else r = mid-1;
}
return l;
}

这样时间复杂度就下降到了\(O(nlogn)\)。

AC代码:

#include <cstdio>
#include <algorithm>

int p[1000001],n,d;
int search(int key) {
int l=1,r=n;
while (l<r) {
int mid = (l+r+1)/2;
if (p[mid]<=key) l = mid;
else r = mid-1;
}
return l;
}
int main() {
int ans=0;
scanf("%d%d",&n,&d);
for (int i=1;i<=n;i++)
scanf("%d",&p[i]);
std::sort(p+1,p+n+1);
for (int i=1;i<=n;i++)
ans += search(p[i]+d)-i;
printf("%d\n",ans);
return 0;
}