题意:
1、询问[l,r]区间和
2、修改[l,r]区间的delta:dalta[i] -> int(sqrt(delta[i]))
思路:
1、区间求和和区间修改我们可以考虑相应的数据结构:线段树或树状数组,这里我们选择树状数组
2、开根号的性质:
(1)1 或 0 开根号后数值和对答案的贡献不变,即开根号操作对0 或 1 无意义,可以直接跳过只处理delta>1
对此, 用并查集维护,f[i]表示i及i右面第一个大于1的数(注意每次赋f[i]的时候都要get_father),只对这些数进行修改
(2)delta[i]<=10^9,最多只需要开五次根号就可以变成1
3、修改:(1)dalta[i] ->int(sqrt(delta[i]))
(2) 并不能直接让树状数组c[i]->int(sqrt(c[i]))
i位置的修改对答案的影响为: -(delta[i]-int(sqrt(delta[i])))
4、注意:long long
var
n,m,x,l,r :longint;
i :longint;
d,f :array[0..100010] of longint;
c :array[0..100010] of int64;
function lowbit(x:longint):longint;
begin
exit(x and (-x));
end;
function get_father(x:longint):longint;
begin
if f[x]=x then exit(x);
f[x]:=get_father(f[x]);
exit(f[x]);
end;
procedure add(x,v:longint);
begin
while (x<=n) do
begin
c[x]:=c[x]+int64(v);
inc(x,lowbit(x));
end;
end;
function find(x:longint):int64;
var
ans:int64;
begin
ans:=0;
while (x>0) do
begin
ans:=ans+c[x];
dec(x,lowbit(x));
end;
exit(ans);
end;
procedure modify(l,r:longint);
var
i:longint;
begin
i:=l;
while (i<=r) do
begin
add(i,-d[i]+trunc(sqrt(d[i])));
d[i]:=trunc(sqrt(d[i]));
if (d[i]<=1) then f[i]:=get_father(i+1);
i:=get_father(i+1);
end;
end;
begin
read(n);
for i:=1 to n+1 do f[i]:=i;
for i:=1 to n do
begin
read(d[i]);
add(i,d[i]);
end;
read(m);
for i:=1 to m do
begin
read(x,l,r);
if x=1 then
begin
writeln(find(r)-find(l-1));
end else modify(l,r);
end;
end.
——by Eirlys