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