可持久化線段樹
之所以說這種結構可持久化,是因為它儲存了歷史版本資訊。對於可持久化線段樹來說,我們可以找到任何歷史修改版本的區間資訊。
不論是單點修改,還是區間修改,線段樹+懶惰標記就決定了每次修改的複雜度是 o(logn) 級別的,也就是說,每次修改只會改變 logn 個結點的值。那麼我們對於當前的新線段樹,就只用建立 logn 個新結點,所以總空間複雜度是 o(nlogn) 。
下圖是乙個維護區間最小值的線段樹,紅色結點表示當執行「將第二個元素修改為1」的操作時建立的新結點。
這樣的乙個資料結構,對於每乙個根結點,我們都可以「提取」出乙個完整的線段樹,所以我們分析的時候,可以看成有 n 棵完整的線段樹,並且每棵線段樹都有一樣的結構。另外這裡的兒子結點編號再也不滿足 2k 和 2k+1 的規律,所以我們要記錄每個結點的兒子編號。
主席樹主席樹就是一棵掛滿了主席(比如學生會主席)的樹。
主席樹也叫做可持久化權值線段樹,表示它是一種可持久化線段樹,並且每棵線段樹都是權值線段樹。
主席樹通常用於解決區間第 k 小的問題,我們建立乙個可持久化的權值線段樹,並且第 q 棵權值線段樹(也就是對應根結點 rt[q] )記錄的是數列前 q 項的資訊,也就是數列前 q 項中每個數出現了多少次。這裡可以看出主席樹維護的是乙個字首資訊。然後區間第 k 小的處理過程就有字首差分的思想,因為出現次數是可以作差的,所以對於區間 [l,r] 的資訊,我們可以用線段樹 rt[r] 減去線段樹 rt[l-1](這裡的減法運算是指對應線段樹的每個結點的值作差),然後在上面尋找第 k 小的數即可。
一般來說我們都要預處理出涉及到的所有數,然後用離散化的方法。
下面是主席樹的模板,對應題目 第k小數 或者題目 可持久化線段樹1 :
#include
#define mem(a,b) memset(a,b,sizeof(a))
#define rep(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define rep_(i,a,b) for(int i=(a);i>=(b);i--)
#define pb push_back
using
namespace std;
typedef
long
long ll;
typedef vector<
int> vi;
intread()
return flag?x:
-x;}
template
<
class
t>
struct president_tree
intbuild_
(int l,
int r)
intupdate_
(int k,
int l,
int r,
int loc)
if(loc<=mid) l[now]
=update_
(l[k]
,l,mid,loc)
;else r[now]
=update_
(r[k]
,mid+
1,r,loc)
; t[now]
=t[l[now]
]+t[r[now]];
return now;
} t query_
(int l,
int r,
int ll,
int rr,
int rk)
t query
(int l,
int r,
int rk)};
const
int maxn=
2e5+5;
int a[maxn]
;int
main()
return0;
}
總結 可持久化資料結構
做了一下可持久化資料結構。總結一下。2.影魔 好久以前的題了。現在才會。要求兩種貢獻必須都要被統計。考慮線段樹掃瞄線。首先用單調棧維護出左右離這個數最近的大於這個數的數的位置,得到區間 l i,r i 那麼有三種貢獻 l i,r i p1 l i和 i 1,r i 1 p2 l i 1,i 1 和r...
可持久資料結構
o n log n 由於很久之前看過這個題,一直以為不是很可做,然而發現一眾大佬全都切掉了,於是好好想了想,發現不是很難。如果 k k 只需要從右到左再跑一遍即可。o n logn 長時間思考無果後去看了個標籤 切比雪夫距離轉曼哈頓距離?然後就去學習了一下,然後就會了。首先將題裡的那個東西轉成曼哈頓...
演算法初探 可持久化資料結構
2020.10.18 16 34 可持久化這個東西啊,簡單的來說就是增加題目難度通過奇妙的方式新建結點來儲存之前的資訊,以達到回退的目的 陣列是我們經常接觸的東西 但是一可持久化就成藍題了 這其實相當於單點修改單點查詢的主席樹 我們會發現一次操作最多更新 lg n 個節點 於是我們把這 lg n 個...