學長推薦了這個博客詳細的介紹了線段樹的建立、查找、更新;
數組計算機
Time Limit: 1000 ms Memory Limit: 65536 KiB
Problem Description
bLue 有一個神器的機器,這個機器可以讀入一個數組,並按照用戶要求快速地進行數組的處理和計算,它支持如下兩種操作:
- 操作 1:把數組中第 p 個元素的值增加 v。
- 操作 2:計算數組中 [l, r] 區間內所有數的和。
這個機器就是這麼的神奇,但是 bLue 的計算機壞掉了,你能幫他修一下嗎?
Input
輸入數據有多組(數據組數不超過 20),到 EOF 結束。
對於每組數據:
- 第 1 行輸入一個整數 n (1 <= n <= 10^5),表示數組中元素的個數。
- 第 2 行輸入 n 個用空格隔開的整數 ai (1 <= ai <= 10^10),表示初始輸入到計算機中的數組。
- 第 3 行輸入一個整數 q (1 <= q <= 50000),表示用戶的操作次數。
- 接下來 q 行,每行輸入先輸入 1 個整數,表示操作類型,根據不同的操作類型:
- 如果類型爲 1,則緊接着輸入 2 個用空格隔開的整數 p (1 <= p <= n) 和 v (1 <= v <= 10^10),表示要把數組中第 p 個數的值增加 v。
- 如果類型爲 2,則緊接着輸入 2 個用空格隔開的整數 l, r (1 <= l <= r <= n),表示要計算區間 [l, r] 內所有數的和(數組下標從 1 開始)。
Output
對於每組數據中的每次類型爲 2 的操作,輸出 1 行,包含一個整數,表示計算出的和。
Sample Input
5 1 2 3 4 5 5 2 1 2 2 1 5 1 4 10 2 4 5 2 1 5
Sample Output
3 15 19 25
補一波學長的總結:
- 首先明確一點,線段樹是一顆完全二叉樹,然後建樹的時候用數組比較方便。
- 假設根節點下標是n,那麼他的左兒子下標是n×2+1,右兒子下標是n×2+2。
- 這裏有個細節是數組是開的結構體數組,因爲一個節點有時候要儲存好幾個信息,這裏我把維護的區間也儲存起來了,方便查詢的時候調用。
- 建樹的時候遞歸建樹就可以。
- 查詢的時候,這裏分兩種情況:如果查詢區間與當前節點的區間無交集,那麼返回。如果查詢區間與當前節點的區間有交集,那麼把查詢區間更新爲這個交集。(具體爲什麼,可以畫圖模擬一下就知道了)
- 最後當前節點是葉子節點的話,就直接返回當前節點的值,否則繼續查詢當前節點的左右子樹並返回兩者返回值的和。
- 更新節點值的時候相當於二分查找,直到找到需要添加值的葉子節點。這裏需要注意的是,查詢過程中,如果沿途上的節點的區間包含要更新的節點的話,順便把這個節點也更新掉。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
struct node
{
int l, r;
long long int data;
};
struct node tree[1234567];
long long int Begin[1234567];
void Buildtree( int root, int l, int r )
{
tree[root].l = l;
tree[root].r = r;
if( l == r )
tree[root].data = Begin[l];
else
{
int mid = ( l + r ) / 2;
Buildtree( root * 2 + 1, l, mid );
Buildtree( root * 2 + 2, mid+1, r );
tree[root].data = tree[root * 2 + 1].data + tree[root * 2 + 2].data;
}
}
long long int Query ( int root, int l, int r )
{
int i = tree[root].l, j = tree[root].r;
if( i > r || j < l )
return 0;
l = max( l, i );
r = min( r, j );
if( i == l && j == r )
return tree[root].data;
return Query( root * 2 + 1, l, r ) + Query( root * 2 + 2, l, r );
}
void Updata ( int root, long long int pos, long long int data )
{
int l = tree[root].l, r = tree[root].r;
if( l > pos || r < pos )
return;
tree[root].data += data;
if( l == r )
return;
Updata( root * 2 + 1, pos, data );
Updata( root * 2 + 2, pos, data );
}
int main()
{
int n;
while( ~scanf("%d", &n) )
{
memset( tree, 0, sizeof( tree ));
memset( Begin, 0, sizeof(Begin));
int i;
for( i=0; i<n; i++ )
scanf("%lld", &Begin[i]);
Buildtree( 0, 0, n-1 );
int q;
scanf("%d", &q);
while( q-- )
{
int type;
long long int x, y;
scanf("%d %lld %lld", &type, &x, &y);
if( type == 1 )
Updata( 0, x-1, y );
if( type == 2 )
printf("%lld\n", Query(0, x-1, y-1));
}
}
return 0;
}