樹狀陣列 單點修改,區間查詢(詳解)

2022-06-02 00:54:08 字數 1893 閱讀 1875

問題的提出 :

給定乙個序列 a,可以進行兩種操作:

(單點修改,區間查詢)

首先,我們會想到直接用乙個現行的陣列。那麼單點修改的時間複雜度將是 o(1

)o(1

),但是區間查詢的時間複雜度卻是 o(n

)o(n

), 資料範圍一大,就很有可能會超時。

那麼,又有人會想到用乙個字首和陣列,但是有沒有想過,雖然區間查詢的時間複雜度變成了 o(1

)o(1

),但單點修改就又變成了 o(n

)o(n

),因為我們在修改單點的時候還要維護這個字首和陣列。

所以,為了優化時間複雜度,我們就需要引入乙個概念:

樹狀陣列:

上面就是一棵樹。可能有同學就要問了:這張圖有用嗎?

非常有用!

每乙個數子的子葉有多少個呢?這麼看是看不出來的。其實每乙個數的個數等於這個數化為二進位制之後,從低位起往前查詢,當找到第乙個 1 的時候,就把 1 和後面的 0 截出來,化成 10 進製,這個十進位制的數就是他子葉的個數(包括他自己)。下面是示例:

但是像上面的操作就很複雜,也會消耗大量的時間,所以我們又可以得到兩個公式:

x - (x & (x - 1)) 或者 x & (-x) ; 這兩個最為常用。

x - (x & (x - 1)) : 我們舉個栗子吧。比如 20 ,它化成 2 進製之後,就是 10010,那麼它減一就是 :10001 再把他們兩個與起來,就是 10000, 再用原數來減,就剛好是 10 ,返回時就會自動轉換成 10 進製了。

可以在草稿紙上嘗試自行證明~

然後我們又可以看到,其實 c[8] = c[4] + c[6] + c[5] + c[8]; 那四個數加起來其實就是c[1~8]的和。

我們就可以寫出乙個迴圈:

void update(int x, long long num_) 

}

關於區間查詢,我們很容易就可以想到字首和。所以a[l~r] = sum® - sum(l - 1)。下面是字首和的**:

long long sum(int x) 

return sum_;

}

所以上面那道題我們就可以輕鬆地解決了。下面是**:

#include #include #include #include using namespace std;

const int maxn = 1e6 + 5;

long long bit[maxn];

int a[maxn];

int n, m;

int lowbit_set(int x) //查詢子葉個數

void update_set(int x, int num)

}//如上

long long sum_set(int x)

return sum_;

}//如上

int main()

for (int i = 1; i <= m; i++)

if (x == 2) //下次記得不要抄**哦!

long long sum1_ = sum_set(y - 1);

long long sum2_ = sum_set(z);

printf("%lld\n", sum2_ - sum1_);//字首和公式

} }/*

for (int i = 1; i <= n; i++) */

//檢查是否寫對

return 0;

}

樹狀陣列 單點修改區間查詢

樹狀陣列,時間複雜度o mlogn 明顯優於暴力列舉以及字首和,主要用於單點修改區間查詢 當然還有區間修改單點查詢 如果一道題中只有區間查詢,那麼建議使用字首和維護 思想直接理解不好理解,借助資料 a陣列下標12 3456 78數值2 5632 714以上是我們要儲存的a陣列,就是原資料 b陣列下標...

樹狀陣列(單點修改區間查詢)

lowbit是用來取出二進位制中最低位數的1所代表的二進位制的值。只需要記下 就行了 int lowbit int x 將乙個樹的最子節點修改,則其父節點也需要更改,父父節點也需要修改。x x lowbit x 就是用來取出其父節點的。void add int x,int k 能查詢原陣列的字首和,...

樹狀陣列 單點修改,區間查詢

本人水平有限,題解不到為處,請多多諒解 本蒟蒻謝謝大家 題目 time limit 10 sec memory limit 512 mb submit 231 solved 78 submit status web board 給定數列 a 1 a 2 a n 你需要依次進行 q個操作,操作有兩類 ...