树状数组(oj1978 & 1979)

本文深入讲解了树状数组的基本概念及其在解决特定问题中的应用。通过对比线段树,突出了树状数组在时间和空间效率上的优势,并通过具体实例演示了如何使用树状数组进行区间查询和更新操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目1
题目2

开始BB

树状数组,类似于线段树,只不过处理问题范围相对小一些(???)
不过,优点在于时间,空间和代码复杂度都小得多。
对比一下(a是原数组):
线段树:
这里写图片描述
树状数组:
这里写图片描述


设tree为c
根据图可以知道,c[i]=a[i-2^k+1]+…+a[i] (k为i二进制下结尾0的个数)
设i=10=1010 (2)
因为结尾有1个0,所以10的父节点就是有2个0的节点。
即12 =1100(2)
很显然可以知道,只需要把10+2 (二进制下为10),就可以得到12。
2即等于2^1。
所以一个节点的父节点即为i+2^k。


比如我要求从1~10的节点和,要怎样用树状数组计算?
根据上面可以得知,树中一个节点的控制范围是在i-2^k+1~i。
那么把i减去2^k,可以得到?~i-2^k的范围 (?为新的i的k)
可以发现,这两个区间没有任何重合,中间也没有任何空缺。
所以可以直接把区间的和加上。
所以一个区间的下一个区间的结尾是i-2^k。


还有,如何求k?
还是以10为例:
10=1010 (2)

科普一下:
一个数的相反数的二进制等于它的补码+1
补码:即0变1,1变0
所以
+10=0000…01010
-10=1111…10110
是不是发现了什么???
如果一个数为00….00100…000,
那么反过来为11….11011…111
再加上一,为11….11100…000
后k+1为是相同的!
而且第k+1位一定为1,后面的都为0
所以很显然,K=i and (-i)

自己理解


所以上面给出的两道题目,解法就不细讲了
都是用树状数组搞一搞,然后算答案就行了

1978(模板题):

var
        d:array[1..100000] of longint;
        t:array[1..100000] of longint;
        power:array[1..100000] of longint;
        n,m,i,j,k,l,x,y:longint;
        ch:char;
procedure change(x,s:longint);
begin
        while x<=n do
        begin
                t[x]:=t[x]+s;
                x:=x+power[x];
        end;
end;
begin
        readln(n);
        for i:=1 to n do
        begin
                readln(t[i]);
                d[i]:=t[i];
        end;
        for i:=1 to n do
        power[i]:=i and (-i);
        for i:=1 to n-1 do
        if i+power[i]<=n then
        t[i+power[i]]:=t[i+power[i]]+t[i];
        readln(m);
        for i:=1 to m do
        begin
                read(ch);
                readln(j,k);
                if ch='Q' then
                begin
                        x:=0;
                        y:=0;
                        l:=j-1;
                        while l>=1 do
                        begin
                                x:=x+t[l];
                                l:=l-power[l];
                        end;
                        l:=k;
                        while l>=1 do
                        begin
                                y:=y+t[l];
                                l:=l-power[l];
                        end;
                        writeln(y-x);
                end
                else
                begin
                        change(j,k-d[j]);
                        d[j]:=k;
                end;
        end;
end.

1979:

var
        t:array[1..32001] of longint;
        p:array[1..32001] of longint;
        n,i,j,k,x,y:longint;
begin
        readln(n);
        for i:=1 to 32001 do
        p[i]:=i and (-i);
        for i:=1 to n do
        begin
                readln(x,y);
                inc(x);
                j:=x;
                k:=0;
                while x>=1 do
                begin
                        k:=k+t[x];
                        x:=x-p[x];
                end;
                writeln(k);
                while j<=32001 do
                begin
                        inc(t[j]);
                        j:=j+p[j];
                end;
        end;
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值