1945. 奶牛棒球

摘要
Title: 1945. 奶牛棒球
Tag: 二分、双指针
Memory Limit: 64 MB
Time Limit: 1000 ms

Powered by:NEFU AB-IN

Link

1945. 奶牛棒球

  • 题意

    农夫约翰的 N 头奶牛排成一排,每头奶牛都位于数轴中的不同位置上。
    它们正在练习投掷棒球。
    农夫约翰观看时,观察到一组三头牛 (X,Y,Z) 完成了两次成功的投掷。
    牛 X 把球扔给她右边的牛 Y,然后牛 Y 把球扔给她右边的牛 Z。
    约翰指出,第二次投掷的距离不少于第一次投掷的距离,也不超过第一次投掷的距离的两倍。
    请计算共有多少组牛 (X,Y,Z) 可能是约翰所看到的。

  • 思路

    • 二分
      先枚举其中两个点,然后二分第三个点,N为1e6,O(n2logn)O(n^2logn)是可以过的
    • 双指针
      找端点l和r,l求的是大于等于2yx2y-x的最小值,r求的是大于3y2x3y-2x的最小值,每次枚举这两个端点即可
  • 代码

    有两版二分

    • 手写二分

      • 找第一个大于等于x的下标
      • 找最后一个小于等于x的下标

      这两个二分属于是非常经典的例子了
      所以范围是两端闭合的,故是 (k2 - k1 + 1)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      '''
      Author: NEFU AB-IN
      Date: 2022-01-16 12:07:46
      FilePath: \ACM\Acwing\1945.py
      LastEditTime: 2022-01-16 22:34:58
      '''
      from bisect import bisect_left, bisect_right

      n = int(input())
      lst = []


      def erfen1(j, x):
      l = j
      r = n - 1
      while l < r:
      mid = l + r >> 1
      if lst[mid] >= x: #二分的位置在mid左边或mid的位置,由于可能有多个,所以r = mid把所有符合的包含进去
      r = mid
      else:
      l = mid + 1
      return r


      def erfen2(j, x):
      l = j
      r = n - 1
      while l < r:
      mid = l + r + 1 >> 1 #因l = mid, 需要上取整
      if lst[mid] <= x:
      l = mid
      else:
      r = mid - 1
      return r


      if __name__ == '__main__':
      for i in range(n):
      x = int(input())
      lst.append(x)
      lst.sort()
      res = 0
      for i in range(n - 2):
      for j in range(i + 1, n - 1):
      x = lst[j] - lst[i]
      # k1 = bisect_left(lst, lst[j] + x)
      # k2 = bisect_right(lst, lst[j] + 2 * x)
      # res += (k2 - k1)
      k1 = erfen1(j + 1, lst[j] + x)
      k2 = erfen2(j + 1, lst[j] + 2 * x)
      # 由于可能出现端点就不满足的情况,所以需要判断
      if lst[k1] >= lst[j] + x and lst[k2] <= lst[j] + 2 * x:
      res += (k2 - k1 + 1)
      print(res)

    • 调用函数

      • 找第一个大于等于x的下标
      • 找第一个大于x的下标,其实就相当于上面求的,只不过这里边界取不到

      所以范围是左端闭合,右端不闭合的,故是 (k2 - k1)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      '''
      Author: NEFU AB-IN
      Date: 2022-01-16 12:07:46
      FilePath: \ACM\Acwing\1945.py
      LastEditTime: 2022-01-16 22:34:58
      '''
      from bisect import bisect_left, bisect_right

      n = int(input())
      lst = []


      def erfen1(j, x):
      l = j
      r = n - 1
      while l < r:
      mid = l + r >> 1
      if lst[mid] >= x: #二分的位置在mid左边或mid的位置,由于可能有多个,所以r = mid把所有符合的包含进去
      r = mid
      else:
      l = mid + 1
      return r


      def erfen2(j, x):
      l = j
      r = n - 1
      while l < r:
      mid = l + r + 1 >> 1 #因l = mid, 需要上取整
      if lst[mid] <= x:
      l = mid
      else:
      r = mid - 1
      return r


      if __name__ == '__main__':
      for i in range(n):
      x = int(input())
      lst.append(x)
      lst.sort()
      res = 0
      for i in range(n - 2):
      for j in range(i + 1, n - 1):
      x = lst[j] - lst[i]
      k1 = bisect_left(lst, lst[j] + x)
      k2 = bisect_right(lst, lst[j] + 2 * x)
      res += (k2 - k1)
      # k1 = erfen1(j + 1, lst[j] + x)
      # k2 = erfen2(j + 1, lst[j] + 2 * x)
      # if lst[k1] >= lst[j] + x and lst[k2] <= lst[j] + 2 * x:
      # res += (k2 - k1 + 1)
      print(res)

    • 双指针

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      '''
      Author: NEFU AB-IN
      Date: 2022-02-09 16:59:54
      FilePath: \ACM\Acwing\1945.1.py
      LastEditTime: 2022-02-09 17:06:01
      '''

      lst = []

      if __name__ == '__main__':
      n = int(input())
      for i in range(n):
      x = int(input())
      lst.append(x)
      lst.sort()
      res = 0
      for i in range(n - 2):
      for j in range(i + 1, n - 1):
      x = lst[j] - lst[i]
      l = j + 1 #两个端点从j+1开始往后取
      r = j + 1
      while l < n and lst[l] < lst[j] + x:
      l += 1
      while r < n and lst[r] <= lst[j] + 2 * x: #小于等于就往后走
      r += 1
      res += r - l
      print(res)
使用搜索:谷歌必应百度