这道题就很优秀了。考试的时候完全没想到正解,最后写了一个暴力20分…
直接讲分块吧。我们预处理两个数组 S t e p [ ] , G e t [ ] Step[],Get[] Step[],Get[],其中 S t e p [ i ] Step[i] Step[i]表示第 i i i个数跳到下一块需要的步数, G e t [ I ] Get[I] Get[I]表示第 i i i个数跳到下一块中的哪一个数,这样我们就只需要分块处理就可以了。
再讲一下预处理与查询与修改吧。
关于预处理:我们从后往前扫,判断一下当前的这个数与这个数直接跳的数是否处于同一个块中,如果是的话就直接 S t e p [ x ] = S t e p [ x + J u m p [ x ] ] + 1 , G e t [ x ] = G e t [ x + J u m p [ x ] ] Step[x]=Step[x+Jump[x]]+1,Get[x]=Get[x+Jump[x]] Step[x]=Step[x+Jump[x]]+1,Get[x]=Get[x+Jump[x]],不然的话就 S t e p [ x ] = 1 , G e t [ x ] = x + J u m p [ x ] Step[x]=1,Get[x]=x+Jump[x] Step[x]=1,Get[x]=x+Jump[x]。时间复杂度: O ( n ) O(n) O(n)
关于查询:我们从起点 x x x开始,先将步数记录 A n s + = S t e p [ x ] Ans+=Step[x] Ans+=Step[x],然后再跳过去 x = G e t [ x ] x=Get[x] x=Get[x],就这样循环一直到 G e t [ x ] = 0 Get[x]=0 Get[x]=0。时间复杂度: n \sqrt{n} n
关于修改:修改的话就最开始 J u m p [ x ] = y Jump[x]=y Jump[x]=y,然后我们循环 f o r ( i = x ; i > = L e f t [ B l o c k [ x ] ] ; i − − ) for(i=x;i>=Left[Block[x]];i--) for(i=x;i>=Left[Block[x]];i−−)预处理操作了。时间复杂度: n \sqrt{n} n
总时间复杂度: O ( n + m n ) O(n+m\sqrt{n}) O(n+mn)
参考代码:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define DB double
#define SG string
#define LL long long
using namespace std;
const LL Max=2e5+5;
const LL Mod=1e9+7;
const LL Inf=1e18;
LL N,M,S,SNum,L[Max],R[Max],Get[Max],Step[Max],Jump[Max],Block[Max];
inline LL Read(){
LL X=0;char CH=getchar();bool F=0;
while(CH>'9'||CH<'0'){if(CH=='-')F=1;CH=getchar();}
while(CH>='0'&&CH<='9'){X=(X<<1)+(X<<3)+CH-'0';CH=getchar();}
return F?-X:X;
}
inline void Write(LL X){
if(X<0)X=-X,putchar('-');
if(X>9)Write(X/10);
putchar(X%10+48);
}
void Update(LL X,LL Y){
LL I;Jump[X]=Y;
for(I=X;I>=L[Block[X]];I--){
if(Block[I]==Block[I+Jump[I]]){
Step[I]=Step[I+Jump[I]]+1,Get[I]=Get[I+Jump[I]];
} else {
Step[I]=1,Get[I]=I+Jump[I];
}
}
}
LL Calc(LL X){
LL Ans=0;
while(true){
Ans+=Step[X];
X=Get[X];
if(X==0){
return Ans;
}
}
}
int main(){
LL I,J,K;
N=Read();S=sqrt(N);
SNum=N/S;
if(N%S!=0){
SNum++;
}
for(I=1;I<=SNum;I++){
L[I]=(I-1)*S+1;
R[I]=I*S;
}R[SNum]=N;
for(I=1;I<=N;I++){
Jump[I]=Read();
Block[I]=(I-1)/S+1;
}
for(I=N;I;I--){
if(I+Jump[I]>N){
Step[I]=1;
} else if (Block[I]==Block[I+Jump[I]]) {
Step[I]=Step[I+Jump[I]]+1;Get[I]=Get[I+Jump[I]];
} else {
Step[I]=1,Get[I]=I+Jump[I];
}
}
M=Read();
for(I=1;I<=M;I++){
K=Read();
if(K==1){
LL X=Read()+1;
Write(Calc(X)),putchar('\n');
} else {
LL X=Read()+1,Y=Read();
Update(X,Y);
}
}
return 0;
}