參考部落格 前言
如果完全掌握了主席樹的前置知識,主席樹其實也是一種並不算很難的資料結構(雖然蒟蒻還是花了好久)。主席樹主要的前置知識就是權值線段樹,一旦理解了權值線段樹的相關知識,那麼主席樹的學習應該也會變得較為簡單。
權值線段樹是線段樹的一種,但是它與普通線段樹不同的地方在於,普通線段樹節點的區間代表的是序列中的區間,而權值線段樹節點的區間則代表了序列中值的區間。
如這樣乙個序列:1 4 2 3 3
普通線段樹中我們通常以序列下標為範圍來進行線段樹的區間劃分,而在權值線段樹中這個陣列是這樣儲存的:
其中每個節點的值是這個序列在這個值域範圍內一共有多少個數。
讓我們想象這樣兩顆權值線段樹:一顆以序列[1,x]為序列建樹,另一顆以[1,n]為基礎序列建樹,同時都用相同的範圍建樹,這樣的兩棵樹每個節點所代表的值域是完全相同的。這也就是說,如果我們用第二棵樹的某個節點的權值去減去第一棵樹對應節點的權值,所得到的值便應該是[x+1,n]序列中該節點值域範圍內數的數量,這也就是權值線段樹的可加減性。
通過權值線段樹及其性質,我們不難想到如何查詢區間[l,r]範圍的第k大數:只需要針對序列a的每乙個字首都建立一顆權值線段樹即可。
然而這樣所需要的空間又過於龐大了,需要一定的優化。
通過觀察兩棵相鄰權值線段樹的結構,可以發現以下規律:
那麼我們就可以讓沒有變化的節點指向到之前建好的樹上,並且新建與前一棵樹上不同路徑上的節點即可。
那麼這樣我們就得到了建立靜態主席樹的方法,下面是靜態主席樹模板題及**
hdu 2665
#include
using
namespace std;
typedef
long
long ll;
#define lson (root << 1)
#define rson (root << 1 | 1)
const
int n =
5e6+10;
int a[n]
, b[n]
;int t[n]
, rs[n]
, ls[n]
, sum[n]
, cnt;
void
build
(int
&root,
int l,
int r)
void
update
(int
&root,
int l,
int r,
int pre,
int data)
intquery
(int s,
int e,
int l,
int r,
int k)
intmain()
sort
(b +
1, b + n +1)
; m =
unique
(b +
1, b + n +1)
- b -1;
cnt =0;
build
(t[0],
1, m)
;//建立一棵空樹
for(
int i =
1; i <= n; i++
) a[i]
=lower_bound
(b +
1, b + m +
1, a[i]
)- b;
//離散化後進行下標的對應
for(
int i =
1; i <= n; i++
)update
(t[i],1
, m, t[i -1]
, a[i]);
//每乙個數建立一棵樹
while
(q--)}
return0;
}
可持久化線段樹 主席樹(靜態)
參考部落格 先介紹一下主席樹,主席樹也稱函式式線段樹也稱可持久化線段樹。其實就是支援查詢歷史版本,這個在看完之後就會了解 其實主席樹就是很多線段樹組合的總體,從它的其它稱呼也可以看出來了,其實它本質上還是線段樹。主席樹就是利用函式式程式設計的思想來使線段樹支援詢問歷史版本 同時充分利用它們之間的共同...
可持久化陣列(主席樹)
模板 includeusing namespace std typedef long long ll define mid l r 1 const int inf 1e9 7,maxnode 24e6 7,maxn 1e6 7 int n,m,tmp maxn rt maxn intval maxn...
主席樹(可持久化線段樹)
我真弱。連主席樹都不會。主席樹相當於多個線段樹,由於相鄰兩棵線段樹的節點的值只有少許不同,因此可以對於和前一棵樹一樣的子樹乙個指標指過去,無需操作,這樣每棵樹o logn 總複雜度o nlogn 以下是區間k大 include include include define n 100005 defi...