luogu3374 【模板】树状数组

luogu

警告:此处需要一定的二进制基础,二进制问题请右转百度

树状数组的储存有两个数组a[i],c[i],a[i]表示第i个数的大小
而c[i]则比较神奇,用下图表示
树状数组
c[i]的地址(也就是i)最低位的位数决定了c[i],所能包含的数的多少,具体关系为:

c[i]包含数的个数==lowbit(i)的返回值

lowbit(i)返回的是i中最低位1的位置,可以通过机器补码的特性来完成

int lowbit(int x)
{
    return x&-x;
}

构造一个初始的树状数组时,可以通过i+lowbit(i)的操作,像拨算盘进一位一样。保证得到的新数组的lowbit值更大且更高位相同。如图,
lowbit

for(int i=1;i<=n;i++)
{
    int x=i;
    while(x<=n)
    {
        c[x]+=a[i];
        x+=lowbit(x);
    }
}

更新一个a[x]时,同时所有”lowbit值比x大且更高位相同”的数,也可以通过lowbit函数实现

int x,k;
scanf("%d%d",&x,&k);
int tmp=x;
a[x]+=k;
while(tmp<=n)
{
    c[tmp]+=k;
    tmp+=lowbit(tmp);
}

询问[x,y]区间和时,可以将问题拆分为求[1,x)和[1,y]的区间和,并相减。此处也可以通过lowbit函数实现
以[1,7]的区间求和为例,7(10)=111(2),所以x-=lowbit(x)会等于4,6,7,用图一表示为
这里写图片描述

int x,y;
scanf("%d%d",&x,&y);
int tmp,res;
tmp=y,res=0;
while(tmp>0)
{
    res+=c[tmp];
    tmp-=lowbit(tmp);
}
tmp=x-1;
while(tmp>0)
{
    res-=c[tmp];
    tmp-=lowbit(tmp);
}
printf("%d\n",res);

完整代码如下:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=500010;
int a[maxn],c[maxn];
int lowbit(int x)
{
    return x&-x;
}
int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)
    {
        int x=i;
        while(x<=n)
        {
            c[x]+=a[i];
            x+=lowbit(x);
        }
    }
    for(int i=1;i<=m;i++)
    {
        int num;
        scanf("%d",&num);
        if(num==1)
        {
            int x,k;
            scanf("%d%d",&x,&k);
            int tmp=x;
            a[x]+=k;
            while(tmp<=n)
            {
                c[tmp]+=k;
                tmp+=lowbit(tmp);
            }
        }
        else
        {
            int x,y;
            scanf("%d%d",&x,&y);
            int tmp,res;
            tmp=y,res=0;
            while(tmp>0)
            {
                res+=c[tmp];
                tmp-=lowbit(tmp);
            }
            tmp=x-1;
            while(tmp>0)
            {
                res-=c[tmp];
                tmp-=lowbit(tmp);
            }
            printf("%d\n",res);
        }
    }
    return 0;
}

写的不好,如有问题,欢迎在评论区指出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值