洛谷P1296 奶牛的耳语

题目描述

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

输入格式

第一行包含两个整数 n,d

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

输出格式

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

输入输出样例

输入 #1
5 10
10 12 16 37 40
输出 #1
4

说明/提示

数据规模

对于 40% 的数据,1≤n≤10^3

对于100% 的数据,1≤n≤10^6

 

这是第一道我没有看别人的题解和思路自己写的二分题,所以我现在成就感up,于是赶紧写一篇博客(记录曾经优秀的我)

 

思路:首先看到这是一道入门题(不要问我为什么会做红题),第一感觉是这个题肯定很水,于是我根本没看取值范围,直接就用了两层循环来做,发现只得了81分,T了两个点。这时候我才发现好像两层循环不能过,n的取值范围是≤10^6,如果套两层循环然后一个个去比较他们之间距离的绝对值是否<d,那么很显然,这是O(n^2)的复杂度,而这样就是O(10^12),很显然这是会裂开的,所以说就要用到二分查找来做。记得当初学分治的时候确实有点难理解。我这次不去刻意的背代码,就是根据自己的理解与思考去写,发现居然A了这个题,说明有的时候还是要相信自己(ง •_•)ง

下面说一下二分:其实就跟书上的二分查找差不多,划定左右区间,然后找左右端点与当前这个点之间的距离的关系。首先我没有用绝对值(可能是我太菜了),那么就要分类讨论,分别将当前奶牛的两边分成两部分,即左边和右边分别二分查找。首先说左边,那么右端点就是当前位置-1,因为你不能和自己交流QWQ,左端点显然是1(我从1开始输入的数组),然后使mid=(left+right)/2,看看mid这个位置和当前位置的距离跟d的关系。如果当前位置与mid的位置距离大于d,那么mid左边的奶牛肯定也和当前位置的奶牛距离大于d,那么你就不用去找了,直接使left=mid+1,这也是二分的精髓之处,可以大大优化时间复杂度。反之,如果距离≤d,那么就说明左边可能还有可以交流的,就继续往左边找,即right=mid-1。就这样一直找一直找,当left>right的时候查找结束,那么这个时候到底left的位置是最远的可以交流的奶牛还是right的位置呢?当时学分治的时候我也有这个疑问。但是其实你只要认真思考一下,就能够绕过这个弯来,我可以分享一下我的理解方法。如果到倒数第二步,距离left>d,而距离right≤d,那么这时候mid就应该在left的位置,因为c++默认整除不了向下取整,那么此时right=mid-1,此时left=mid+1,right也是=mid+1,此时mid=right=left,距离≤d,那么right=mid-1,此时left=mid>mid-1=right,循环结束,那么显然right的位置距离>d,而left≤d,所以left的位置就是可以交流的最远的那只奶牛,设当前奶牛位置为i,那么从left到i算上他们本身是i-left+1,但是i不能和自己交流,所以减1,那么ans+=i-left。这种情况是mid=left=right的位置距离≤d,如果>d,那么left=mid+1,此时mid的位置就应该是交流不到的最近的那只奶牛的位置,那么如果再向右+1,也就是当前的left,就是可以交流到的最远的那只奶牛的位置,所以还是ans+=i-left。从右边二分的时候也是同样的道理,只不过应该是ans+=right-i,下面,上代码!!!

 

代码:

 

#include<iostream>
#include<cstdio>
#include<math.h>
#include<algorithm>
#include<cstring>
using namespace std;
long long i,d,n,mid,ans;
long long a[1000010];
int main()
{
    cin>>n>>d;
    for(i=1;i<=n;i++)
        cin>>a[i];
    sort(a+1,a+n+1);
    for(i=1;i<=n;i++)
    {
     long long left=1,right=i-1;
     while(left<=right)
     {
      mid=(left+right)/2;
      if(a[i]-a[mid]>d) left=mid+1;
      else right=mid-1;
  }
  ans+=i-left;
  left=i+1,right=n;
  while(left<=right)
  {
   mid=(left+right)/2;
   if(a[mid]-a[i]>d) right=mid-1;
   else left=mid+1;
  }
  ans+=right-i;
 }
 cout<<ans/2;//最后结果千万别忘记除以2,因为每一对奶牛都统计了一次和它能交流的奶牛和能和它交流的奶牛对它的统计
 return 0;
}
 
posted @ 2020-05-13 17:49  徐明拯  阅读(463)  评论(0编辑  收藏  举报