ARC060 E Tak and Hotels

版权声明:本文为博主原创文章,可以转载但是必须声明版权。 https://blog.csdn.net/forever_shi/article/details/83278842

题目链接

题意:
给你 n n 个坐标,要求每次移动不超过 l l ,并且每次移动后一定要到这 n n 个坐标中的某一个。有 q q 次询问,每次询问一对点 a , b a,b ,求从 a a b b 最少移动几次。 n , q < = 1 e 5 n,q<=1e5

题解:
首先我们可以用一个two pointer在O(n)的时间内算出以每个点为起点走不超过 l l 的距离最多走到哪个点。感觉网上好多都说要二分,但是我感觉二分还没有two pointer好写,而且跑得还慢。求完只后我们可以对刚才的数组倍增,求出每个点操作 2 i 2^i 步最多到达的编号是多少。这样对于每次询问,从 a a b b 和从 b b a a 是等价的,所以我们设 b > = a b>=a 。我们从 a a 先跳到小于 b b 的可以跳到的最后一个点,然后再跳一次就能跳到 b b 了,于是就可以用倍增每次 l o g n logn 的复杂度来回答询问了。最终复杂度 O ( n + q l o g n ) O(n+qlogn)

代码:

#include <bits/stdc++.h>
using namespace std;

int n,q,a[100010],f[100010][21],l,r;
int main()
{
 scanf("%d",&n);
 for(int i=1;i<=n;++i)
 scanf("%d",&a[i]);
 scanf("%d",&l);
 r=1;
 for(int i=1;i<=n;++i)
 {
  while(a[r+1]-a[i]<=l&&r+1<=n)
  ++r;
  f[i][0]=r;
 }
 for(int i=n;i>=1;--i)
 {
  for(int j=1;j<=20;++j)
  f[i][j]=f[f[i][j-1]][j-1];
 }
 scanf("%d",&q);
 for(int i=1;i<=q;++i)
 {
  int x,y,ans=0;
  scanf("%d%d",&x,&y);
  if(x>y)
  swap(x,y);
  for(int j=20;j>=0;--j)
  {
   if(f[x][j]<y)
   {
    x=f[x][j];
    ans+=(1<<j);
   }   
  }
  ans+=1;
  printf("%d\n",ans);
 }
 return 0;
}

猜你喜欢

转载自blog.csdn.net/forever_shi/article/details/83278842