题解 CF797E Array Queries


分析

根号分治题。

发现 \(p\) 是单增的,且每次至少增加 \(k\)。所以如果 \(k\) 很大,我们直接暴力跳很少的次数就能跳出去,如果确定了一个 \(k\) 值,我们可以倒序 \(O(n)\) 处理出每个位置作为起点需要跳多少次才能跳出。

如果将前后两个综合起来,在 \(k\) 大于 \(\sqrt{n}\) 时直接暴力跳,次数会小于 \(\sqrt{n}\)。提前预处理所有小于 \(\sqrt{n}\)\(k\) 下所有位置作为起点需跳多少次,只需花 \(O(n\sqrt{n})\) 的时间即可全部处理,所以总时间最多为 \(O((n+m)\sqrt{n})\),可以通过此题。

代码

#include
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);
		}
	}
}