[BZOJ3038&&BZOJ3211] 势能分析线段树

势能分析线段树是这样一个东西,对于某一些操作,不滋磁打标记,只能暴力更改但操作很少次以后就不会改变结果了(最常见的就是区间开根号),我们可以维护一些东西来表示这个区间是否会改变。
例子:
区间开根号,维护min和max,若min>=0且max<=1就不管。
区间整除,维护min和max,若min+1=max,直接变成区间修改或区间加(雅礼集训day1 T1)。

代码:

type
  tree=^treenode;
  treenode=record
    l,r,max,min:longint;
    sum:int64;
    ls,rs:tree;
  end;

var
  n,i,q,opt,l,r:longint;
  delta:array[0..100100]of longint;
  xtr:tree;
function max(x,y:longint):longint;
begin
  if x<y then exit(y)
  else exit(x);
end;
function min(x,y:longint):longint;
begin
  if x>y then exit(y)
  else exit(x);
end;
procedure updata(x:tree);
begin
  x^.max:=max(x^.ls^.max,x^.rs^.max);
  x^.min:=min(x^.ls^.min,x^.rs^.min);
  x^.sum:=x^.ls^.sum+x^.rs^.sum;
end;

procedure build(x:tree;l,r:longint);
var
  mid:longint;
begin
  x^.l:=l;
  x^.r:=r;
  if l=r then
  begin
    x^.max:=delta[l];
    x^.min:=delta[l];
    x^.sum:=delta[l];
    x^.ls:=nil;
    x^.rs:=nil;
    exit;
  end;
  mid:=(l+r)div 2;
  new(x^.ls);new(x^.rs);
  build(x^.ls,l,mid);
  build(x^.rs,mid+1,r);
  updata(x);
end;
function ask(x:tree;l,r:longint):int64;
var
  mid:longint;
begin
  if (x^.l=l)and(x^.r=r) then exit(x^.sum);
  mid:=(x^.l+x^.r)div 2;
  if r<=mid then exit(ask(x^.ls,l,r));
  if l>mid then exit(ask(x^.rs,l,r));
  exit(ask(x^.ls,l,mid)+ask(x^.rs,mid+1,r));
end;
procedure change(x:tree;l,r:longint);
var
  mid:longint;
begin
  if (x^.max<=1)and(x^.min>=0) then exit;
  if x^.l=x^.r then begin x^.sum:=trunc(sqrt(x^.sum)); x^.max:=x^.sum; x^.min:=x^.sum; exit; end;
  mid:=(x^.l+x^.r)div 2;
  if r<=mid then change(x^.ls,l,r)
  else if l>mid then change(x^.rs,l,r)
    else
    begin
      change(x^.ls,l,mid);
      change(x^.rs,mid+1,r);
    end;
  updata(x);
end;
begin
  readln(n);
  for i:=1 to n do
    read(delta[i]);
  new(xtr);
  build(xtr,1,n);
  readln(q);
  for i:=1 to q do
  begin
    readln(opt,l,r);
    if opt=1 then writeln(ask(xtr,l,r))
    else change(xtr,l,r);
  end;
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值