首页 > 代码库 > 【codevs2333】&【BZOJ2002】弹飞绵羊[分块]

【codevs2333】&【BZOJ2002】弹飞绵羊[分块]

  我其实是在codevs上看到它的题号后才去做这道题的。。。2333。。。

  题目传送门:codevs:http://codevs.cn/problem/2333/  bzoj:http://www.lydsy.com/JudgeOnline/problem.php?id=2002

  题意已经说的很清晰了,蓝鹅……我不会做。

  看题解有两种写法:分块和link-cut tree。但是lct我不会,只能用分块了。

  但是怎么分块呢?

  我们想一下有什么暴力写法。一种是记录每个反弹装置能往后弹多长距离,这样效率是修改O(1)+查询O(n)。另一种是记录从每个反弹装置开始到被弹飞要被弹多少次,这样效率是修改O(n)+查询O(1)。我们可以想一个介于两者之间的做法,比如……把序列平均分成m块,记录每个反弹装置弹到下一个块要弹多少次 和 会弹到下一个块的哪一个装置。这样一来,效率就变成了修改O(n/m)+查询O(m),总时间复杂度就是O(n*(n/m+m))。显然,如果要使效率最快m就取√n。

  接下来就可以打代码+AC了。(代码真心很短)

代码:

技术分享
var a,ne,x,y:array[0..200010]of longint;
  n,m,i,j,k,p,t,q,ans:longint;
begin
  read(n); p:=trunc(sqrt(n));
  for i:=0 to n-1 do begin
    read(a[i]); ne[i]:=(i div p+1)*p;
  end;
  for i:=0 to n-1 do begin
    x[i]:=i; y[i]:=0;
    while(x[i]<ne[i])and(x[i]<n)do begin
      x[i]:=x[i]+a[x[i]]; inc(y[i]);
    end;
  end;
  read(m);
  for i:=1 to m do begin
    read(q);
    if q=1 then begin
      read(k); ans:=0;
      while k<n do begin
        ans:=ans+y[k]; k:=x[k];
      end;
      writeln(ans);
    end
    else begin
      read(k,t); a[k]:=t;
      for j:=ne[k]-1 downto ne[k]-p do
        if j+a[j]>=ne[j] then begin
          x[j]:=j+a[j]; y[j]:=1;
        end
        else begin
          x[j]:=x[j+a[j]]; y[j]:=y[j+a[j]]+1;
        end;
    end;
  end;
end.
弹飞绵羊

 

【codevs2333】&【BZOJ2002】弹飞绵羊[分块]