主席樹的原理其他部落格已經寫的很詳細了,我就不加贅述了。
主要是記錄一下個人的部分理解以及模板,以便以後**
主席樹是一種可持久化資料結構,或者說,可持久化線段樹,利用主席樹
可以檢視線段樹的歷史狀態並對其修改
具體做法就是每個點開一顆線段樹
為了節省空間採取動態開點
建樹的時間複雜度是\(o(nlogn)\)的,單次查詢,插入都是\(o(logn)\)的
總複雜度為\(o((n+m)log n)\) m為操作次數
下面是模板
#include#include#include#include#include#define ll long long
using namespace std;
/*主席樹為了節省空間,採取的是動態開點
與動態開點相同,l,r維護的是左右兒子下標
不是區間邊界
*/const int maxn=200010;
int t;
int n,m;
int q;
int tot;
int cnt;
int a[maxn];
int t[maxn];
int b[maxn];
struct nodetre[maxn<<5];
inline int read()
while(ch<='9'&&ch>='0')
return f*ret;}/*
注意,build和update返回值是該樹的根
所以我們的rt不能用全域性變數
*/int build(int l,int r)
int mid=(l+r)>>1;
tre[rt].l=build(l,mid);
tre[rt].r=build(mid+1,r);
return rt;}/*
主席樹的精髓,每乙個點建一顆線段樹
為了節省空間,對樹進行重複利用。
具體如何節省空間?
我們只開需要更改值的點
不需要更改值的點直接連上就好
*/ int update(int t,int l,int r,int k)
else
return rt;
}int query(int le,int re,int l,int r,int k)
int x=tre[tre[re].l].sum-tre[tre[le].l].sum;
int mid=(l+r)>>1;
if(x>=k)//求區間第k大,因為維護的區間是有序的,所以加入左區間點數大於k,那麼區間第k大一定在左側
else
}int main()
m=unique(b+1,b+1+n)-b-1;//為什麼我們需要減1?陣列首元素需要考慮
sort(b+1,b+1+n);
t[0]=build(1,m);
for(int i=1;i<=m;i++)
int x,y,z;
while(q--)
return 0;
}
主席樹 模板
思想 主席樹就是一顆持久化線段樹,為什麼叫持久化了,因為它可以儲存之前的線段樹版本,並且可以拿來用,從而優化空間.至於為什麼叫主席樹了,大概是因為發明這個演算法的人的名字的緣故吧 詳細說說 主席樹是一種離線資料結構,是由很多棵線段樹組成的。第i棵線段樹存的是前i個數的資訊 每乙個線段存數字的出現次數...
主席樹模板
維護n棵1 i的字首權值線段樹,每次查詢減一下就好了。poj 2104就是模板題,裸的靜態第k大,需要先離散化,不會的就用lower bound 多試試,研究研究應該就能懂。include include include include using namespace std const int m...
模板 主席樹
譴責奶人的hsz巨神 靜態版本 求區間第k大 可持久化也沒那麼高大上嘛,主席樹本質上就是多棵線段樹,求第k大就類似平衡樹的第k大。stay foolish,stay hungry,stay young,stay include include include include using namesp...