分析
根号分治题。
发现 \(p\) 是单增的,且每次至少增加 \(k\)。所以如果 \(k\) 很大,我们直接暴力跳很少的次数就能跳出去,如果确定了一个 \(k\) 值,我们可以倒序 \(O(n)\) 处理出每个位置作为起点需要跳多少次才能跳出。
如果将前后两个综合起来,在 \(k\) 大于 \(\sqrt{n}\) 时直接暴力跳,次数会小于 \(\sqrt{n}\)。提前预处理所有小于 \(\sqrt{n}\) 的 \(k\) 下所有位置作为起点需跳多少次,只需花 \(O(n\sqrt{n})\) 的时间即可全部处理,所以总时间最多为 \(O((n+m)\sqrt{n})\),可以通过此题。
代码
#include<bits/stdc++.h>
using namespace std;
inline void read(int &res){
res=0;
int f=1;
char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')res=(res<<1)+(res<<3)+c-48,c=getchar();
res*=f;
}
int n,m,k;
int a[100005];
int f[100005][321];
int main()
{
read(n);k=sqrt(n);
for(int i=1;i<=n;i++){
read(a[i]);
}
for(int j=1;j<=k;j++){//预处理
for(int i=n;i>=1;i--){
if(i+a[i]+j>n)f[i][j]=1;
else f[i][j]=f[i+a[i]+j][j]+1;
}
}
read(m);
while(m--){
int x,y;
read(x);read(y);
if(y<=k){//提取预处理的答案
printf("%d\n",f[x][y]);
}
else {//暴力跳
int ti=0;
while(x+a[x]+y<=n){
ti++;
x=x+a[x]+y;
}
printf("%d\n",ti+1);
}
}
}