势能分析线段树是这样一个东西,对于某一些操作,不滋磁打标记,只能暴力更改但操作很少次以后就不会改变结果了(最常见的就是区间开根号),我们可以维护一些东西来表示这个区间是否会改变。
例子:
区间开根号,维护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.