線段樹的修改

2022-08-21 20:24:12 字數 1570 閱讀 2066

單點修改就很簡單啦,直接遞迴到葉子節點修改就行

void modify(int k, int l, int r, int x, int v)

int mid = (l + r ) >> 1;

if(x <= mid)

modify(ls(k), l, mid, x, v);

else

modify(rs(k), mid + 1, r ,x ,v);

tree[k] = tree[ls(k)] + tree[rs(k)];

return;

}

有時候我們不止解決單點修改,單點詢問,還要解決區間修改,區間詢問。

我們如果把乙個區間拆分成單點修改最壞情況下,複雜度可以達到\(o(mn\log n)\)還不如樸素演算法。

於是,我們引入一種新的方法——延遲標記

我們在每個節點上維護乙個值\(tag[k]\)表示該節點所轄區間裡所有的數的值都為\(tag[k]\)同時,我們改變節點的值\(tree[k]\)為\(tag[k] * (l + r - 1)\)。

但是,如果我們不需要繼續向下遞迴,我們就不對下面的子節點進行修改。

例如, 我們將下圖中\([2,9]\)的數字都改成\(v\)。就給綠色節點打上標記,表示區間內的數字都為已經改為\(v\)。且區間和為\((l + r - 1) * v\)

但是, 如果我們要查詢他子節點的值該如何呢?

首先, 我們只改變了\(k\)的值。但是,如果我們不繼續向下遞迴,我們\(k\)的孩子節點就不會被改變。

換句話說,在這裡,\(k\)的值是修改後的值,但是\(k\)的子節點是沒有修改的,但是,因為我們只需要\(k\)的值,所以我們不需要對其孩子的值進行修改。

在我們需要查詢其孩子的值時再將\(k\)的標記下傳到他的左右兒子,並將其上的標記清零。

每次向下遞迴都要下傳標記!!!,不管你是單點還是區間,查詢還是修改,都要下傳!因為你要將子節點的值改為正確值才能再次操作。

這裡以區間修改為例。

void add(ll k, ll l, ll r, ll v)//改標記

void push_down(ll k, ll l, ll r, ll mid)//標記下傳

void change(ll k, ll l, ll r, ll x, ll y, ll v)//區間修改

ll mid = (l + r) >> 1;

push_down(k, l, r, mid);//每次遞迴都要下傳!!!

if(x <= mid)

change(ls(k), l, mid, x, y, v);

if(y > mid)

change(rs(k), mid + 1, r, x, y, v);

tree[k] = tree[ls(k)] + tree[rs(k)];

}

因為我暫時不會標記永久化所以這部分內容之後會補齊。

完結撒花!!!

線段樹的修改

對於一棵 最大線段樹,每個節點包含乙個額外的max屬性,用於儲存該節點所代表區間的最大值。設計乙個modify的方法,接受三個引數root index和value。該方法將 root 為跟的線段樹中 start,end index,index 的節點修改為了新的 value 並確保在修改後,線段樹的...

線段樹的修改

對於一棵最大線段樹,每個節點包含乙個額外的max屬性,用於儲存該節點所代表區間的最大值。設計乙個modify的方法,接受三個引數root index和value。該方法將 root 為根的線段樹中 start,end index,index 的節點修改為了新的 value 並確保在修改後,線段樹的每...

線段樹的區間修改

1 include 2 include3 define lc p 1 該節點的左孩子 4 define rc p 1 1 右孩子5 using namespace std 67 很類似與樹狀陣列 8int n,m 9struct segment tree 10tree 1000000 線段樹陣列 1...