bzoj 3211 树状数组+并查集

19 篇文章 0 订阅
15 篇文章 0 订阅

题意:

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值