【AtCoder Regular Contest 060 E】Tak and Hotels
倍增。
题目大意:
给定 $n$ 个旅店在数轴上的位置(从左到右),和一天最多行驶的距离 $L$。
多组询问,每次给出 $a,b$,问从 $a$ 至少几天才能到 $b$。每天的终点必须是一个旅店。
题解:
容易想到倍增。
用 $f_{i,j}$ 表示走 $2^i$ 天,从 $j$ 出发能到达的最右边的点。
由于 $L$ 是固定的,所以每次从最右边的点继续走一定是最优的。
所以预处理的时候,$f_{i,j}=f_{i-1,f_{i-1,j}}$ 即可。
往左走同理。
求答案就类似树上求 LCA 的操作,从大到小枚举 $i$,如果 $2^i$ 天还没到就跳过去。最后还得再补 $1$。
时间复杂度 $O((n+q)\log n)$。
Code:
#include<cstdio>
const int N=1e5+9;
int L[17][N],R[17][N],n,t,q,X[N],ans;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i)scanf("%d",X+i);
scanf("%d",&t);
for(int i=1,it=1;i<=n;++i){
while(it<n&&X[it+1]-X[i]<=t)++it;
R[0][i]=it;
}
for(int i=n,it=n;i;--i){
while(it>1&&X[i]-X[it-1]<=t)--it;
L[0][i]=it;
}
for(int i=1;i<17;++i)
for(int j=1;j<=n;++j)
R[i][j]=R[i-1][R[i-1][j]],L[i][j]=L[i-1][L[i-1][j]];
for(scanf("%d",&q);q--;printf("%d\n",ans)){
int x,y;ans=1;
scanf("%d%d",&x,&y);
if(x<=y){
for(int i=16;~i;--i)
if(R[i][x]<y)ans+=1<<i,x=R[i][x];
}else{
for(int i=16;~i;--i)
if(L[i][x]>y)ans+=1<<i,x=L[i][x];
}
}
return 0;
}