ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

树状数组简介

2019-06-21 20:47:52  阅读:225  来源: 互联网

标签:25 树状 int 简介 sum place 数组 lowbit


简单讲一下我对树状数组的理解
avatar
c[i]等于它所管辖区域的a的和(或者其他的)
这就是树状数组的原理
树状数组的优势在于代码好写,常数较小
缺点是适用范围较小,只适用于可以从\([1,r]\)和\([1,l]\)推出\([l,r]\)的问题
本质上就是利用了倍增的思想


以下代码以求前缀和(单点修改,区间查询)为例,数组下标从1开始,a[i]为初始数组,c[i]为树状数组

1.最基本的\(O(1)\)lowbit
这可以得到当前数字二进制表示下的最后一个1是代表的多大的数
例如\(25_{(2)}=11001 \quad lowbit(25)=1\),\(24_{(2)}=1100 \quad lowbit(24)=8\)

int lowbit(int x)
{
    return x&(-x);//eg:25=11001,-25=00110(按位取反)+1=00111,25&(-25)=1(与运算就是两个二进制数按位比较,都为1则为1)
}

2.\(O(\log_{2}{n})\)修改

void modify(int place,int val)
{
    for(int i=place;i<=n;i+=lowbit(i))//i+=lowbit(i)可以这么理解:把最后一个1的位置加一个1并进位,如6会变成8,5会变成6等,对照上面的图相当于往上走一级
            c[i]+=val;
}

3.\(O(\log_{2}{n})\)查询

int getsum(int x)//求[1,x]的和
{
    int ans=0;
    for(int i=x;i;i-=lowbit(i))//比如求[1,6]的和,就相当于先加上[5,6](c[6]),再加上[1,4](c[4])
        ans+=c[i];
    return ans;
}

求\([l,r]\)的和就相当于先求出\([1,r]\)的和再减去\([1,l-1]\)的和
是不是板子极其好背啊


再就是区间修改
利用差分
设差分数组为\(d[i]=a[i]-a[i-1] \quad d[1]=a[1]\)

\begin{split}
\sum_{i=1}^{x} a[i] & =\sum_{i=1}^{x} \sum_{j=1}^{i}d[j] \newline
& =\sum_{i=1}^{x}(x-i+1)* d[i] \newline
& =(x+1)* \sum_{i=1}^{x}d[i]-\sum_{i=1}^{x}i* d[i] \newline
\end{split}
所以我们只要维护两个树状数组c[i]为a[i]的前缀和,c2[i]为\(i*a[i]\)所产生的新数列的前缀和
代码只需在初始化时\(\text modify(i,a[i]-a[i-1])\),查询时再设一个\(\text ans2+=i*c2[i]\)并\(\text return (x+1)*ans-ans2\)

注意在修改\([l,r]\)时是在l修改+val,r+1是修改-val(因为这是一个差分数组)

两道模板题洛谷P3374 洛谷P3368
一般这种题都要开long long
贴两个代码

// 单修区查,P3374
#include<bits/stdc++.h>
using namespace std;
#define N 500005
long long a[N],c[N];
int n,m,y,z,op;
int lowbit(int i)
{
    return i&(-i);
}
void modify(int place,int value)
{
    for(int i=place;i<=n;i+=lowbit(i))
        c[i]+=value;
}
int getsum(int place)
{
    int ans=0;
    for(int i=place;i;i-=lowbit(i))
        ans+=c[i];
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&op);//节省一个变量
        modify(i,op);
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&op,&y,&z);
        if(op==1)
            modify(y,z);
        else
            printf("%d\n",getsum(z)-getsum(y-1));
    }
    return 0;
}
// 区修单查(和上一道题结合就是区修区查),P3368
#include<bits/stdc++.h>
using namespace std;
#define N 500005
long long a[N],c[N],c2[N];
int n,m,x,y,z,op;
int lowbit(int i)
{
    return i&(-i);
}
void modify(int place,int value)
{
    for(int i=place;i<=n;i+=lowbit(i))
        c[i]+=value,c2[i]+=place*value;
}
int getsum(int place)
{
    int ans1=0,ans2=0;
    for(int i=place;i;i-=lowbit(i))
        ans1+=c[i],ans2+=c2[i];
    return (place+1)*ans1-ans2;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);//也可设两个变量而不用数组,略麻烦一点
        modify(i,a[i]-a[i-1]);
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&op);
        if(op==1)
            scanf("%d%d%d",&y,&z,&x),modify(y,x),modify(z+1,-x);
        else
            scanf("%d",&x),printf("%d\n",getsum(x)-getsum(x-1));
    }
    return 0;
}

标签:25,树状,int,简介,sum,place,数组,lowbit
来源: https://www.cnblogs.com/123789456ye/p/11066743.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有