[ 數(shù)據(jù)結(jié)構(gòu) ] 樹狀數(shù)組

樹狀數(shù)組

  • 前置知識 :
  1. 差分&前綴和

  2. 位運算

  3. 樹的基本概念和定理

1. 什么是樹狀數(shù)組?

樹狀數(shù)組(Binary Indexed Tree(B.I.T), Fenwick Tree)是一個查詢和修改復(fù)雜度都為Log(N)的數(shù)據(jù)結(jié)構(gòu)。主要用于查詢?nèi)我鈨晌恢g的所有元素之和,但是每次只能修改一個元素的值;經(jīng)過簡單修改可以在Log(N)的復(fù)雜度下進行范圍修改,但是這時只能查詢其中一個元素的值(如果加入多個輔助數(shù)組則可以實現(xiàn)區(qū)間修改與區(qū)間查詢)?!俣劝倏?/p>

SMG

其實基本的樹狀數(shù)組就是實現(xiàn)這樣一個問題

——求
  1. 數(shù)組中一段區(qū)間的和
  2. 修改數(shù)組中某一個數(shù)

小明:"不就是前綴和可以搞定的嘛!"

\large\texttt{嗖---}

#include<iostream>
using namespace std;
int a[1000000];
int sum[1000000];
int main()
{
    int n, ask;
    cin >> n >> ask;
    while (ask --)
    {
        char type;
        cin >> type;
        if (type == 'A') //查詢
        {
            int x;
            cin >> x;
            cout << sum[x] << endl;
        }
        else if (type == 'B') //修改
        {
            int x, p;
            cin >> x >> p;
            for (int i = x; i <= n; i ++) sum[i] += p;
        }
    }
    return 0;
}

\large\texttt{提交}

image

跑得好快呀!

那么這時我們的樹狀數(shù)組就該出場啦~

首先,樹狀數(shù)組,顧名思義長得像樹的數(shù)組

所以樹狀數(shù)組長這個樣

image

上面是樹,下面是數(shù)組

首先是求區(qū)間和

通過這幅圖,應(yīng)該不難看出,一個數(shù)到最前面的距離就等于剛好包含這個區(qū)間的幾個橫條的和:

比如區(qū)間1~7為:

image

綠色條的和

知道這個,那么任意兩個數(shù)之間的區(qū)間也就容易了,
只需要把兩個區(qū)間到1的區(qū)間和相減就可以了。

那有了區(qū)間和還不夠,還要修改呢

修改也簡單,只需要每次修改包含自己的橫條就好了(廢話)

比如修改5的值:

image

把藍色橫條全改了就好啦!

這么說兩個功能都準(zhǔn)備好了,那就到代碼實現(xiàn)了

首先引入一個概念——Lowbit

指一個數(shù)在二進制下最末尾的1所對應(yīng)的值

十進制數(shù) 二進制數(shù) Lowbit值
1 \large\texttt{1} 1
2 \large\texttt{1} 0 2
3 1\large\texttt{1} 1
4 \large\texttt{1}00 4
5 10 \large\texttt{1} 1
6 1 \large\texttt{1} 0 2
7 11 \large\texttt{1} 1
8 \large\texttt{1} 000 8
9 100\large\texttt{1} 1
10 10\large\texttt{1}0 2
... ... ...

發(fā)現(xiàn)了嗎,跟它在樹狀數(shù)組中的高度是一模一樣的

image

首先二進制負數(shù)就是原數(shù)的反碼(把原數(shù)的0變?yōu)?,1變?yōu)?)+1(也稱原數(shù)的補碼)

也就是說我們用原數(shù)按位與(&)原數(shù)的補碼

就是最后一個1的位置所表示的值,也就是Lowbit值

綜上,Lowbit(x) = x&(-x)

如果沒看懂也不要緊,記下來就好了。

根據(jù)上面的結(jié)論,我們可以搞定兩個東西

1. 一個位置左邊的橫條在 x - Lowbit(x)

2. 一個位置上面的橫條在 x + Lowbit(x)

(隨便找一個點在上圖中康康就知道啦)

?

有了這幾個結(jié)論,我們就可以開始搞事情了

首先是區(qū)間查詢

只需要一直往左邊找。(上面的1號結(jié)論)

代碼很簡單:

// tree表示橫條的值
int get_sum(int x) // 求前x個數(shù)的和
{
    int sum = 0;
    while (x > 0)
    {
        ans += tree[x]; // 加上當(dāng)前點的值
        x -= lowbit(x); // 繼續(xù)找左邊的一個橫條
    }
    return ans;
}

接著是修改點的值

只需要一直找上一個橫條。(上面的2號結(jié)論)

代碼:

// 這個是加一個數(shù),可以有別的修改
void add(int x, int p)
{
    while (x <= n)
    {
        tree[x] += p; //修改改當(dāng)前點
        x += lowbit(x); // 繼續(xù)找上面的橫條
    }
}

樹狀數(shù)組get!

那它的復(fù)雜度呢?

  • 區(qū)間查詢

從之前所說來看,我們只要往左找,越到左邊層數(shù)越高,最左邊是最高的,最高點的高度最多為這顆二叉樹樹的高度

時間復(fù)雜度: \text{O}(\log_2N)

  • 單點修改

修改單點指需要找到包含自己的所有橫條,也就是一直往樹根跳,小于樹的高度

時間復(fù)雜度: \text{O}(\log_2N)

再看看前綴和的復(fù)雜度:

  • 區(qū)間查詢: 時間復(fù)雜度: \text{O}(1)

  • 單點修改: 時間復(fù)雜度: \text{O}(N)

小明:"怪不得T飛了!"

模板切掉!

洛谷P3374【模板】樹狀數(shù)組1

#include<iostream>
#define endl '\n'
using namespace std;
int a[500010];
int n, m;
int lowbit(int x) //求Lowbit
{
    return x & -x;
}
void add(int x, int k) //修改
{
    while (x <= n)
    {
        a[x] += k;
        x += lowbit(x);
    }
}
int sum(int k) // 求和
{
    int sum = 0;
    while (k > 0)
    {
        sum += a[k];
        k -= lowbit(k);
    }
    return sum;
}
int main()
{
    ios::sync_with_stdio(0);  // 玄學(xué)輸入輸出優(yōu)化
    cin.tie(0);
    cout.tie(0);
    cin >> n >> m;
    for (int i = 1; i <= n; i ++)
    {
        int tmp;
        cin >> tmp;
        add(i, tmp); // 初始邊
    }
    for (int i = 1; i <= m; i ++)
    {
        int type, x, k;
        cin >> type >> x >> k;
        if (type == 1) add(x, k); // 加邊
        else cout << sum(k) - sum(x - 1) << endl; // 輸出區(qū)間和
    }
    return 0;
}

溫馨提示:

  1. 樹狀數(shù)組的題輸入量大,注意用 \large\texttt{快讀}\large\texttt{scanf, printf}
  2. 樹狀數(shù)組的題喜歡爆int,不要沒開 \large\texttt{long long} 見祖宗

會了模板,這幾題可以切了!

Loj P10116 清點人數(shù)

Loj P10117 簡單題

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容