码迷,mamicode.com
首页 > 编程语言 > 详细

树状数组板子

时间:2019-07-03 19:42:37      阅读:121      评论:0      收藏:0      [点我收藏+]

标签:初始   include   owb   hellip   线段树   target   复杂   ref   print   

板子1

板子2

在放板子的代码之前,先讲一下树状数组。

树状数组的作用:

在有修改时可以做到log级别求前缀和

还可以结合差分等神奇的东西食

空间比线段树要省的多,代码量也少的多

在单点查询的时候比线段树快了不是一点(我真的没有拿线段树的板子去拍这两个题)

我们先来看一下树状数组是个什么东西

首先,我们有一个序列A,其次lowbit(x)表示x的二进制表示中,最低位的1和后面的0构成的数。

树状数组c[i]记录序列A的区间[i-lowbit(i)+1,x]中所有数的和。

why?我们画一画树状数组

技术图片

 

它是不是很像一棵树

同时我们可以发现一些性质:

每个c[x]保存了以它为根的子树中叶节点的和

c[x]的子节点数=lowbit(x)

c[x]的父亲是c[x+lowbit(x)]

 树深度为logn

占用空间:O(n)

lowbit好像很重要的样子,so,lowbit怎么求呢

lowbit(x)=x&(~x+1)=x&-x,因为~x+1=-x。

why?

举个例子(设x>0,这里牵扯到树状数组不能处理下标为0的情况)

技术图片

树状数组支持两种神奇的修改与查询。

一.单点修改,区间查询(板子1)

二.区间修改,单点查询(板子2)

先说一说第一种

前面提到过c[x]的父亲是c[x+lowbit(x)],所以更改的代码就是这样:

void update(int x,int v)
{
    for(;x<=n;x+=x&(-x))c[x]+=v;
}

因为c数组是维护的前缀和,所以当查询区间[l,r]的时候,我们可以算出sum(r)-sum(l-1),这里sum(i)是[1,i]的和。

怎么算sum(i)?

因为c[x]记录的是[lowbit(x)+1,x]这个区间的和,为了统计[1,x],所以我们每次x要减去lowbit(x)。

代码

int sum(int x)
{
    int ans=0;
    for(;x>=1;x-=x&(-x))ans+=c[x];
    return ans;
}

再来说一说第二种神奇的操作(区间修改,单点查询)我真的没有拿线段树的板子做这道题

我们先看一个东西:差分

现在有一个a数组:a[1],a[2]……a[n]

还有一个b数组:b[1]=a[1]-a[0],b[2]=a[2]-a[1]……b[i]=a[i]-a[i-1]

b数组就叫做差分数组。

通过差分数组的定义,我们知道:a[2]=a[1]+b[2]=b[1]+b[2],a[3]=a[2]+b[3]=b[1]+b[2]+b[3]……

我们发现了差分的一个性质:a[i]=b[1]+b[2]+……+b[i]

那差分有什么用呢?

我们想到这里是区间修改,如果把区间中的每一个点当做单点修改,复杂度O(nlogn),好像还不如暴力,而且树状数组也木有懒标记之类的操作,这时就要用到差分数组了。

若我们让[l,r]这个区间每个数都加上k,则对于差分数组来说,会变的只有b[l],b[r+1]。因为a[l]加了k,而a[l-1]不变,所以b[l]增加了k,a[r]增加了k,a[r+1]不变,所以b[r+1]减少k。

当单点查询时,就是询问前缀和(也就是上面差分的性质)

so,我们只是把树状数组维护的对象从a数组变成了它的差分数组b数组而已,其他的不变。

这里放上板子

#include<bits/stdc++.h>//这个是板子2,板子1也差不多
using namespace std;
int n,m,c[5000009],a[500009];
int read()
{
    char ch=getchar();
    int x=0;bool f=0;
    while(ch<0||ch>9)
    {
        if(ch==-)f=1;
        ch=getchar();
    }
    while(ch>=0&&ch<=9)
    {
        x=(x<<3)+(x<<1)+(ch^48);
        ch=getchar();
    }
    return f?-x:x;
}
void update(int x,int v)
{
    for(;x<=n;x+=x&(-x))c[x]+=v;
}
int sum(int x)
{
    int ans=0;
    for(;x>=1;x-=x&(-x))ans+=c[x];
    return ans;
}
int main()
{
    n=read();
    m=read();
    for(int i=1;i<=n;i++)
      a[i]=read();
    for(int i=1;i<=n;i++)
      update(i,a[i]-a[i-1]);  //在初始化时,直接在i的位置加上a[i]-a[i-1]
    for(int i=1;i<=m;i++)
    {
        int cz=read(),x=read();
        if(cz==1)
         {
             int y=read(),k=read();
             update(x,k);
             update(y+1,-k);
         }
        else
          printf("%d\n",sum(x));
    }
}

 

树状数组板子

标签:初始   include   owb   hellip   线段树   target   复杂   ref   print   

原文地址:https://www.cnblogs.com/lcez56jsy/p/11128498.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!