1. 程式人生 > >線段樹基礎:單點更新,區間最值(和)查詢

線段樹基礎:單點更新,區間最值(和)查詢

單點更新,區間查詢

線段樹可以解決一類區間問題,例如最基礎的單點更新,區間最值查詢。
程式碼如下:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 10000; //原始陣列的最大值
int arr[maxn];
struct node
{
    int l, r, mx;//l表示該結點區間的左端點, r表示該結點區間的右端點,mx表示[l, r]上的最大值。

} tr[4 * maxn]; //一般預設定義4倍大小的最大範圍
void build(int d, int l, int r) // d表示構造的結點編號,l與r表示要構造的區間範圍,一般也就是[1, n]了
{ tr[d].l = l, tr[d].r = r; if(l == r) //如果是葉子結點,也就是是[a,a]型別的結點,那最大值就是該點的值 { tr[d].mx = arr[l];//從原陣列賦值,因為原陣列的下標其實對應了該葉子結點,其實就可以用l代表下標 return;//注意終止返回 } int mid = (l + r) / 2; int ld = 2 * d;//通過陣列來作為樹的實現方式,表示左孩子編號 int rd = 2 * d + 1;//表示右孩子節點編號 build(ld, l, mid);//遞迴構造左孩子的線段樹
build(rd, mid + 1, r);//遞迴構造右孩子線段樹 tr[d].mx = max(tr[ld].mx, tr[rd].mx);//取左右孩子的各自區間的最大值作為該結點區間的最大值 } int query(int d, int l, int r) //d表示從該結點開始查詢,l, r表示要查詢的區間範圍 { if(tr[d].l==l && tr[d].r == r) //如果正正好好要查詢的區間與該結點的區間相等,那麼返回最大值。 { return tr[d].mx; } int mid = (tr[d].l + tr[d].r) / 2
; int ld = 2 * d; //同上 int rd = 2 * d + 1; //同上 if(mid >= r) return query(ld, l, r); //要查詢的右端點小於該結點區間的一半,則去左孩子找 else if(l > mid) return query(rd, l, r);//要查詢的左端點大於該結點區間的一半,則右孩子找 else return max(query(ld, l, mid), query(rd, mid + 1, r));//要查詢的區間有一些在左孩子,有一些在右孩子,則都找一遍,取最大值。 } void update(int d, int pos, int val)//此處是單點更新,d表示從該結點開始更新,pos表示要更新的位置,val表示將該點的值改為val { if(tr[d].l == tr[d].r && tr[d].l == pos) //找到該點,修改最大值為val { tr[d].mx = val; return; } int mid = (tr[d].l + tr[d].r) / 2; int ld = 2 * d;//同上 int rd = 2 * d + 1;//同上 if(pos <= mid) update(ld, pos, val);//更新相應的孩子區間 else update(rd, pos, val); tr[d].mx = max(tr[ld].mx, tr[rd].mx);//更新該結點區間 } int main() { arr[0] = 4, arr[1] = 3, arr[2] = 7, arr[3] = 123, arr[4] = 19; arr[5] = -123, arr[6] = 3, arr[7] = 24, arr[8] = 124, arr[9] = 0; build(1, 0, 9);//因為區間是0~9 printf("%d\n", query(1, 0, 0)); printf("%d\n", query(1, 0, 9)); printf("%d\n", query(1, 0, 5)); update(1, 0, 125); printf("%d\n", query(1, 0, 0)); printf("%d\n", query(1, 0, 9)); return 0; }

練習題:
HDU1166 敵兵佈陣
HDU1754 Hate It
HDU1394 Minimum Inversion Number
HDU2795 Billboard