bzoj2002 弹飞绵羊

| OI, CSDN

分为$\sqrt{n}$左右块,next[i]记录从i跳出当前块的位置,time[i]记录相应次数,up返回当前点所在块最大值,low返回最小值。提前把lowup存起来似乎更快。。。开始先把nexttime处理出来,有修改只修改所在的块,询问一路跳过去就好。注意最后位置的边界处理,以及编号从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.

原文地址