mdzz刚写了一次不知道怎么不见了。。醉了。 算了重新写一次吧,lct出门左转不送~~ Description
某天,Lostmonkey发明了一种超级弹力装置,为了在他的绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏。游戏一开始,Lostmonkey在地上沿着一条直线摆上n个装置,每个装置设定初始弹力系数ki,当绵羊达到第i个装置时,它会往后弹ki步,达到第i+ki个装置,若不存在第i+ki个装置,则绵羊被弹飞。绵羊想知道当它从第i个装置起步时,被弹几次后会被弹飞。为了使得游戏更有趣,Lostmonkey可以修改某个弹力装置的弹力系数,任何时候弹力系数均为正整数。
Input
第一行包含一个整数n,表示地上有n个装置,装置的编号从0到n-1,接下来一行有n个正整数,依次为那n个装置的初始弹力系数。第三行有一个正整数m,接下来m行每行至少有两个数i、j,若i=1,你要输出从j出发被弹几次后被弹飞,若i=2则还会再输入一个正整数k,表示第j个弹力装置的系数被修改成k。对于20%的数据n,m<=10000,对于100%的数据n<=200000,m<=100000
Output
对于每个i=1的情况,你都要输出一个需要的步数,占一行。
Sample Input
4
1 2 1 1
3
1 1
2 1 1
1 1
Sample Output
2
3 提议还是很好理解的吗,很容易想到用分块的方法,其实说实话,只要你会分块,基本上也就等于会了块状链表。。 具体看我代码注释:
var i,j,p,n,m,x,y:longint; f,go,bl,k:array[0..200000]of longint;//f:跳去的点编号,go:跳去的块编号,bl[i]表示第i个点所在的块 //k:跳到下一块所需的步数 procedure init; var i,j,tot,p,sq,p1:longint; begin readln(n); sq:=trunc(sqrt(n)); p:=sq; tot:=0; for i:=1 to n do begin //预处理分块 if p=sq then begin p:=1; inc(tot); bl[i]:=tot; end else begin bl[i]:=tot; inc(p); end; read(f[i]); f[i]:=f[i]+i;//直接加上便于处理 end; //对于有跳跃关系的每两个点进行处理。 for i:=n downto 1 do begin p:=f[i]; if (bl[i]<>bl[p])or(p>n)then//如果跳出本块或跳出界 begin go[i]:=p; k[i]:=1; end else begin//如果在本块内直接继承后一个点的信息 go[i]:=go[p]; k[i]:=k[p]+1; end; end; end; function ask(x:longint):longint;//统计答案比较白痴就不用说了。 var i,j,ans:longint; begin ans:=0; while x<=n do begin inc(ans,k[x]); x:=go[x]; end; exit(ans); end; procedure change(x,y:longint); var i,j,p,tot:longint; begin j:=bl[x]; f[x]:=y; p:=y; //先对点进行预处理时的关系改变 if (bl[p]<>bl[x])or(p>n)then begin go[x]:=p; k[x]:=1; end else begin go[x]:=go[p]; k[x]:=k[p]+1; end; //暴力重构当前块处理方法和上面一样 for i:=x downto 1 do begin p:=f[i]; if bl[i]<>bl[x] then break;//因为是倒着处理,只要不在本块内后面的都不在本块内,自己手玩一下就知道。 if bl[p]=bl[i] then begin go[i]:=go[p]; k[i]:=k[p]+1; end else begin go[i]:=p; k[i]:=1; end; end; end; begin init; readln(m); for i:=1 to m do begin read(p); if p=1 then begin readln(x); inc(x);//题目中说了编号是0到n-1 writeln(ask(x)); end else begin readln(x,y); inc(x); y:=y+x; change(x,y); end; end; end.