分为sqrt(n)左右块,next[i]记录从i跳出当前块的位置,time[i]记录相应次数,up返回当前点所在块最大值,low返回最小值。提前把low、up存起来似乎更快。。。开始先把next、time处理出来,有修改只修改所在的块,询问一路跳过去就好。注意最后位置的边界处理,以及编号从0开始。
我又脑残地多次打错下标。。。
const maxn=290099;
var
j,n,m,i,ans,d,l:longint;
next,time,k,up,low:array[0..maxn]of longint;
function min(a,b:longint):longint;inline;begin if a<b then exit(a);exit(b);end;
//function up(i:longint):longint;inline;begin exit(((i-1)div l+1)*l);end;//
//function low(i:longint):longint;inline;begin exit((i-1)div l*l+1);end;
procedure init;
var i,t:longint;
begin
next[n]:=n+1;
time[n]:=1;
for i:=1 to n-1 do begin next[i]:=i+k[i];time[i]:=1;end;
for i:=n-1 downto 1 do
begin
t:=i;
while t<=min(up[i],n) do
begin
if i<>t then inc(time[i],time[t]);
t:=next[t];
end;
next[i]:=t;
end;
end;
procedure change(x,d:longint);
var i,t:longint;
begin
k[x]:=d;
for i:=min(n-1,up[x]) downto low[x] do begin next[i]:=i+k[i];time[i]:=1;end;
for i:=min(n-1,up[x]) downto low[x] do//x ! i!
begin
t:=i;
while t<=up[i] do//这样能过oj数据,但自己随便拍一下,n、m大于10就常常卡死。。很显然,应该是t<=min(up[i],n)
begin
if i<>t then inc(time[i],time[t]);
t:=next[t];
end;
next[i]:=t;
end;
end;
begin
//assign(input,'in');reset(input);
//assign(output,'out');rewrite(output);
readln(n);
for i:=1 to n do read(k[i]);
l:=trunc(sqrt(n));
for i:=1 to n do begin up[i]:=((i-1)div l+1)*l;low[i]:=(i-1)div l*l+1;end;
init;
readln(m);
while m<>0 do
begin
dec(m);
read(j);
case j of
1:
begin
readln(i);
inc(i);
ans:=0;
while i<=n do
begin
inc(ans,time[i]);
i:=next[i];
end;
writeln(ans);
end;
2:begin readln(i,d);inc(i);change(i,d);end;
end;
end;
close(input);close(output);
end.