線段樹-單點更新-hihoCoder1077

線段樹

線段樹是一種二叉搜索樹,類似區間樹,是一個完全二叉樹,它在各個節點保存一條線段(數組中的一段子數組),主要用於高效解決連續區間的動態查詢問題,它基本能保持每個操作的時間複雜度爲O(lgN)。

假定根結點是長度爲2^h的區間,第i層有2^i個結點(層數從0開始),每個結點對應一個長度爲2^(h-i)的區間。最大層編號爲h,所以結點總數爲1+2+4+……+2^h=2^(h+1)-1;所以一般線段樹開結構體時需要兩倍空間。
當整個區間長度不是2的整數冪時,葉子不全在同一層,但樹的最大層編號和結點總數仍滿足上述結論。
對於線段樹中的每一個下標爲i非葉子節點[a,b],它的左兒子下標爲2*i表示的區間爲[a,(a+b)/2],右兒子下標爲2*i+1表示的區間爲[(a+b)/2+1,b]。因此線段樹是平衡二叉樹,最後的子節點數目爲N,即整個線段區間的長度。

可以邊看下面的圖邊理解我說的話。

把上面的圖和話理解了基本就瞭解線段樹了,接下來看看線段樹的代碼。
線段樹有單點更新和區間更新兩種情況,我以hihoCoder1077爲例講一下線段樹的單點更新。

hihoCoder

題目鏈接

這題就是線段樹單點更新的裸題。就是可以動態修改區間中的一個數,動態詢問區間最小值。具體看代碼註釋

#include<bits/stdc++.h>
using namespace std;

const int INF = 0x3f3f3f3f;
const int maxn = 1e6+5;
//一般線段樹需要開兩倍空間,這裏1e6大概是2的20次方,所以我開2^21
const int MAX = 1<<21; 
//node_id[i]是區間中第i個數在線段樹中的節點編號
int n,q,a,b,c,ans,node_id[maxn];

struct segment_tree{
    int l,r,w;  //l,r區間左右邊界,w區間最小值
}tree[MAX];

void build_segment_tree(int i,int l,int r) //建立線段樹
{
    tree[i].l=l;
    tree[i].r=r;
    tree[i].w=INF;
    if(l==r) //葉子節點
    {
        node_id[l] = i; //保存葉子節點在線段樹中的節點編號
        return;
    }
    //從該節點向下建立線段樹
    build_segment_tree(i*2,l,(l+r)/2);
    build_segment_tree(i*2+1,(l+r)/2+1,r);
}

void update_segment_tree(int i) //線段樹單點更新(從下向上更新)
{
    if(i==1) return;
    int tmp = i/2;
    tree[tmp].w = min(tree[tmp*2].w,tree[tmp*2+1].w);
    update_segment_tree(tmp);
}

int query_segment_tree(int i,int l,int r) //線段樹查詢區間最小值
{
    if(tree[i].l==l&&tree[i].r==r) return ans = min(tree[i].w,ans);
    if(l<=tree[i*2].r)
    {
        //查詢區間全部在該節點的左子節點區間中
        if(r<=tree[i*2].r) query_segment_tree(i*2,l,r);
        //查詢區間包含該節點的左右子節點區間
        else
        {
            query_segment_tree(i*2,l,tree[i*2].r);
            query_segment_tree(i*2+1,tree[i*2+1].l,r);
        }
    }
    //查詢區間全部在該節點的右子節點中
    else query_segment_tree(i*2+1,l,r);
}

int main()
{
    scanf("%d",&n);
    build_segment_tree(1,1,n);
    for(int i=1;i<=n;i++)
    {
        //葉子節點的最小值置爲它本身的值
        scanf("%d",&tree[node_id[i]].w);
        //從葉子節點向上更新線段樹
        update_segment_tree(node_id[i]);
    }
    scanf("%d",&q);
    while(q--)
    {
        scanf("%d%d%d",&a,&b,&c);
        if(a==0)
        {
            ans = INF;
            query_segment_tree(1,b,c);//查詢區間最小值
            printf("%d\n",ans);
        }
        else
        {
            tree[node_id[b]].w = c;//動態修改節點最小值
            update_segment_tree(node_id[b]);
        }
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章