【树状数组】【单点修改区间求和】【区间修改单点查询】【单点修改区间最大值查询】

树状数组:
管辖系统(暂且这么叫吧):
将数组里的数类似树一般管理起来,图片什么的网上可以找到一堆,按照图片来分析,仔细看会发现并不像二叉树之类的树一般整齐,而是树的树枝长度是参差不齐的,这是它使用类似二分实现log复杂度的性质所决定的,每个位置所管辖的区域都是自己二进制表示时 最右边的1 表示的大小 的长度,例如 7(十进制)=111(二进制),他管辖范围是1(二进制),也就是他本身,6(十进制)=110(二进制),他管下的范围是10(二进制)=2(十进制),也就是管辖长度为2,他所管辖的位置是5,6。树状数组中每一项的值也就是原数列中其管辖范围内的和。求管辖范围的操作也就是其核心 lowbit 。

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

区间求和:

树状数组基本操作,假如要求前i项和,我们首先直接加上该位置所管辖的范围,即该位置的值。接着加他所管不到的范围即 i - lowbit(i)位置的值(为什么是 i - lowbit(i),假如1—6区间和,你已经加上了6 管辖部分(长度为2)的和,这个时候当然要去计算1–4区间的和,4 = 6 - 2 )一直递归求和加完即可。l,r 区间求和即前 r 项和减前 l - 1 项和

int sum(int x)
{
    int tmp=0;
    while(x)
    {
        tmp+=t[x];
        x-=lowbit(x);
    }
    return tmp;
}

单点修改:

基本操作 。要改某一位置的值,必须得同时改管辖它的值,否则求和操作就会出现问题。怎么找离他最近的管辖它的值,使用 i + lowbit(i)(以 i=1010000(二进制)举例,要想管住它,肯定要是比它大的数,设为s, 则 s - lowbit(s)< i 若选择了 s=1011xxx,即使最好的情况下 即 1011000 管辖的范围最远也无法涉及它 因此我们发现最少 加上 0010000(二进制) 才能让 i 找到自己的父亲,当然这个解释并不是利用树状数组原理,而是为了理解为何加lowbit(i)可以找到自己最近的父亲)。

int add(int x,int v)
{
    while(x<=n)
    {
        t[x]+=v;
        x=x+lowbit(x);
    }
}

区间修改 与 单点查询:

我的做法是再多费空间开了lz数组,即类比线段树上的 lazy 标记。当然网上各种做法都有还有很多,这个方法是因为我对线段树的lazy操作印象比较深。

int ad_l(int x,int v)
{
    while(x)
    {
        lz[x]+=v;
        x-=lowbit(x);
    }   
}
int qu_p(int x)
{
    int ans=a[x];
    while(x<=n)
    {
        ans+=lz[x];
        x+=lowbit(x);
    }
    return ans;
}
        if (id==1) //修改
        {
            scanf("%d%d%d",&x,&y,&v);
            ad_l(x-1,0-v);ad_l(y,v);
        }
        else   //查询
        {
            scanf("%d",&x);
            printf("%d\n",qu_p(x));
        }

单点修改+区间最小(大)值:
实际上每次修改就是“暴力”的枚举能修改后会影响到的父亲,查询时也是从右端点开始,若目前所在项管理的区间比要求的区间长,就枚举小区间,重复如此,一直凑到左端点,复杂度都是 log2n 的平方。 个人感觉用树状数组来搞这类问题其实不如直接用线段树来解决更加方便理解和操作?可能线段树空间上要占的多一点,树状数组时间上费的多一点。

#include
#include
#include
using namespace std;
int n,m,a[500005],h[500005];//h[x]表示管理的区间内数的最小值
int lowbit(int i)
{
    return i&(-i);
}
int add(int x,int v)
{
    a[x]=v;
    while(x<=n)
    {
        int tmp=lowbit(x);
        h[x]=a[x];
        for (int i=1;i2)
        h[x]=min(h[x],h[x-i]);
        x+=tmp;
    }
}
int que(int x,int y)
{
    int ans=999999999;
    while(x<=y)
    {
        ans=min(ans,a[y]);
        y--;
        while(y-lowbit(y)+1>=x && y!=0)
        {
            ans=min(ans,h[y]);
            y-=lowbit(y);
        }
    }
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
    a[i]=9999999,h[i]=99999999;
    for (int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        add(i,a[i]);
    }
    for (int i=1,id,x,y,v;i<=m;i++)
    {
        scanf("%d",&id);
        if (id==1) 
        {
            scanf("%d%d",&x,&y);
            cout<else
        {
            scanf("%d%d",&x,&v);
            add(x,v);
        }
    }
}

你可能感兴趣的:(算法-数据结构,树状数组)