1. 程式人生 > 實用技巧 >主席樹模板以及個人理解

主席樹模板以及個人理解

主席樹模板及個人理解

主席樹的原理其他部落格已經寫的很詳細了,我就不加贅述了。

主要是記錄一下個人的部分理解以及模板,以便以後觀看

主席樹是一種可持久化資料結構,或者說,可持久化線段樹,利用主席樹

可以檢視線段樹的歷史狀態並對其修改

具體做法就是每個點開一顆線段樹

為了節省空間採取動態開點

建樹的時間複雜度是\(O(nlogn)\)的,單次查詢,插入都是\(O(logn)\)

總複雜度為\(O((n+m)log n)\) m為操作次數

下面是模板

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#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 node{
	int l,r,sum;
}tre[maxn<<5];
inline int read(){
	int f=1;
	int ret=0;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')
			f=-f;
		ch=getchar();
	}
	while(ch<='9'&&ch>='0'){
		ret=ret*10+(ch^'0');
		ch=getchar();
	}
	return f*ret;
}
/*
注意,build和update返回值是該樹的根
所以我們的rt不能用全域性變數 
*/
int build(int l,int r){
	int rt=++cnt;
	if(l==r){
		return rt;
	}
	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){
	int rt=++cnt;
//	cout<<rt<<endl;
	tre[rt].l=tre[t].l; 
	tre[rt].r=tre[t].r;
	tre[rt].sum=tre[t].sum+1;//當前樹繼承上一顆樹的狀態
	if(r==l){
		return rt;
	}
	int mid=(l+r)>>1;
	if(k<=mid){
		tre[rt].l=update(tre[t].l,l,mid,k);//更改需要更改的點 
	}
	else{
		tre[rt].r=update(tre[t].r,mid+1,r,k);
	}
	return rt;	
}
int query(int le,int re,int l,int r,int k){
	if(l==r){
		return l;
	}
	int x=tre[tre[re].l].sum-tre[tre[le].l].sum;
	int mid=(l+r)>>1;
	if(x>=k){
		return query(tre[le].l,tre[re].l,l,mid,k);
	}//求區間第k大,因為維護的區間是有序的,所以加入左區間點數大於k,那麼區間第k大一定在左側 
	else{
		return query(tre[le].r,tre[re].r,mid+1,r,k-x);
	}
}
int main(){
	freopen("P3834_9.in","r",stdin);
		n=read();
		q=read();
		for(int i=1;i<=n;i++){
			a[i]=read();
			b[i]=a[i];
		}
		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++){
			a[i]=lower_bound(b+1,b+1+m,a[i])-b;
			t[i]=update(t[i-1],1,m,a[i]);
		}
		int x,y,z;
		while(q--){
			x=read();
			y=read();
			z=read();
			int p=query(t[x-1],t[y],1,m,z);
			cout<<b[p]<<endl;
		}
	return 0;
}