树状数组

树状数组

先给一张树状数组的图片吧,有图有真相。
这里写图片描述
树状数组就长这样↑,因为看上去像一棵树,所以就叫树状数组了。我们用 c 数组来存储a数组的某些内容,从而优化时间复杂度。

如何实现

lowbit

先来了解一个概念 lowbit 。从字面意思上理解:就是最低的二进制位。在树状数组中,这个的意思是:把一个整数化成二进制,最低的“1”表示的数是多少。
例如:如果要求 lowbit(10) 我们可以知道,10化成二进制是1010,最低的“1”是倒数第二位,就是代表2,所以 lowbit(10)=2
这里有个快速求 lowbit 的方法。
lowbit(x)=x&-x

存储方式

c 是一个树状数组,那么c[i]里面存放的就是从 a[i] 开始数,前 lowbit(i) 个数的和。所以画出来的图就像一棵树了。

修改

这里的修改指的是单点修改。
修改 a[i] 的话,我们就需要,修改所有包含了 a[i] 的树状数组元素。就是 c[i+(lowbit(i))] ,然后令 i+lowbit(i)=i ,再修改 c[i+(lowbit(i))] ,一直到大于总数 n 为止。
所以我们可以写出代码:

void invise(int k,int delta)
{
    a[k]+=delta;
    while(k<=n)
    {
        c[k]+=delta;
        k+=lowbit(k);
    }
}

查询

这里的查询指的是区间查询。
区间查询只能查询a[1] a[k]的和。
查询我们需要把[1,k]这个区间分成不想交没有空隙的小区间。
我们要查询 c[ilowbit(i)] ,同样的,令 ilowbit(i)=i ,再查询 c[ilowbit(i)] 。我们可以写出代码:

int getsum( int k)
{
  int t=0;
  while (k>0)
  {
    t+=c[k];
    k- =lowbit(k);
  }
  return t;
}

单点修改,区间查询

就是最简单的树状数组的板子。

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
    int num=0;
    char c=' ';
    bool flag=true;
    for(;c>'9'||c<'0';c=getchar())
    if(c=='-')
    flag=false;
    for(;c>='0'&&c<='9';num=num*10+c-48,c=getchar());
    return flag ? num : -num;
}
#define maxn 508000
int n,m,a[maxn],d[maxn];
int lowbit(int i)
{
    return i&(-i);
}
void revise(int i,int x)
{
    while(i<=n)
    {
        d[i]+=x;
        i+=lowbit(i);
    }
}
long long find(int i)
{
    long long ans=0;
    while(i)
    {
        ans+=d[i];
        i-=lowbit(i);
    }
    return ans;
}
void init()
{
    n=read();m=read();
    for(int i=1;i<=n;i++)
    {
        a[i]=read();
        revise(i,a[i]);
    }
    for(int i=1;i<=m;i++)
    {
        int c=read();
        int l=read();
        int r=read();
        if(c==2) 
        printf("%lld\n",find(r)-find(l-1));
        if(c==1)
        revise(l,r),a[l]+=r;
    }
}
int main()
{
    init();
    return 0;
}

区间修改,单点查询

这里要运用差分数组。但是不是纯差分。因为它是多次修改多次查询,算法复杂度会下降到 O(n2)
差分数组这样计算 cf[i]=a[i]a[i1] ,所以对差分数组求前缀和就是 a <script id="MathJax-Element-23" type="math/tex">a</script>数组中的元素了。所以我们可以在差分数组的基础上维护树状数组。
对原数组的区间修改就成为了对差分数组的单点修改,对原数组的单点查询就称为了对差分数组的区间查询。

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
    int num=0;
    char c=' ';
    bool flag=true;
    for(;c>'9'||c<'0';c=getchar())
    if(c=='-')
    flag=false;
    for(;c>='0'&&c<='9';num=num*10+c-48,c=getchar());
    return flag ? num : -num;
}
#define maxn 530000
int a[maxn];int n,m;
int cf[maxn]; 
int d[maxn];
int lowbit(int i)
{
    return i&-i;
}
void invice(int i,int x)
{
    while(i<=n)
    {
        d[i]+=x;
        i+=lowbit(i);
    }
}
long long find(int i)
{
    long long ans=0;
    while(i)
    {
        ans+=d[i];
        i-=lowbit(i);
    }
    return ans;
}

void init()
{
    n=read();
    m=read();
    for(int i=1;i<=n;i++)
    {
        a[i]=read();
        cf[i]=a[i]-a[i-1];
        invice(i,cf[i]);
    }
    for(int i=1;i<=m;i++)
    {
        int t=read();
        if(t==1)
        {
            int x=read();
            int y=read();
            int k=read();
            invice(x,k);
            invice(y+1,-k);
        }
        if(t==2)
        {
            int x=read();
            printf("%lld\n",find(x));
        }
    }
}
int main()
{
    init();
    return 0;
}

例题——星星(HLoj532)

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
    int num=0;
    char c=' ';
    bool flag=true;
    for(;c>'9'||c<'0';c=getchar())
    if(c=='-')
    flag=false;
    for(;c>='0'&&c<='9';num=num*10+c-48,c=getchar());
    return flag ? num : -num;
}
#define maxn 60050
#define max_location 32001
struct node
{
    int x,y;
}a[maxn];
int d[maxn];
int lowbit(int i)
{
    return i&-i;
}
int n;
void revise(int k,int delta)
{
    while(k<=max_location)
    {
        d[k]+=delta;
        k+=lowbit(k);
    }
}
int find(int k)
{
    int ans=0;
    while(k)
    {
        ans+=d[k];
        k-=lowbit(k);
    }
    return ans;
}
void init()
{
    n=read();
    for(int i=1;i<=n;i++)
    {
        a[i].x=read()+1;
        a[i].y=read()+1;
        revise(a[i].x,1);
        printf("%d\n",find(a[i].x)-1);
    }
}
int main()
{
    init();
    return 0;
}

二维树状数组——打鼹鼠HLoj534

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
    int num=0;
    char c=' ';
    bool flag=true;
    for(;c>'9'||c<'0';c=getchar())
    if(c=='-')
    flag=false;
    for(;c>='0'&&c<='9';num=num*10+c-48,c=getchar());
    return flag ? num : -num;
}
#define maxn 1025
int d[maxn][maxn];
int n,m;
int lowbit(int x)
{
    return x&-x;
}
void invise(int x,int y,int k)
{
    while(x<=n)
    {
        int ty=y;
        while(ty<=n)
        {
            d[x][ty]+=k;
            ty+=lowbit(ty);
        }
        x+=lowbit(x);
    }
}
long long find(int x,int y)
{
    long long ans=0;
    while(x)
    {
        int ty=y;
        while(ty)
        {
            ans+=d[x][ty];
            ty-=lowbit(ty);
        }
        x-=lowbit(x);
    }
    return ans;
}
void init()
{
    n=read();
    m=read();
    int a,b,c,d;
    while(m!=3)
    {
        if(m==1)
        {
            a=read()+1;
            b=read()+1;
            c=read();
            invise(a,b,c);
        }
        if(m==2)
        {
            a=read()+1;
            b=read()+1;
            c=read()+1;
            d=read()+1;
            printf("%lld\n",find(a-1,b-1)+find(c,d)-find(a-1,d)-find(c,b-1));
        }
        m=read();
    }
}
int main()
{
    init();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值