1. 程式人生 > 其它 >學習筆記——線段樹合併

學習筆記——線段樹合併

資料結構

前置資料結構——普通線段樹

動態開點

有的時候線段樹節點很多,並不需要一次全部建完整棵樹,此時需要動態開點。

由於點是動態開的,所以每個點的位元組點不是固定的,需要特意存一下。

權值線段樹

權值線段數維護的不再是區間了,而是一堆桶。

舉個例子,我們現在有一個長度為 10 的陣列 1,5,2,3,4,1,3,4,4,4

\(1\) 出現了 \(2\) 次,\(2\) 出現了 \(1\) 次,\(3\) 出現了 \(2\) 次,\(4\) 出現了 \(4\) 次,\(5\) 出現了 \(1\) 次。

那麼這個線段樹長這樣:

線段數合併

程式碼

namespace XDS{
	ll tot=0;
	struct XDS_{ll lson,rson,val;}tr[N*40];
	#define ls(p) tr[p].lson
	#define rs(p) tr[p].rson
	#define va(p) tr[p].val
	#define bdmd ll mid=(l+r)>>1
	inline ll NewP(ll val){
		++tot;
		ls(tot)=0;
		rs(tot)=0;
		va(tot)=val;
		return tot;
	}
	inline void UpdateP(ll &p,ll l,ll r,ll x,ll val){
		if(!p)p=NewP(0);
		if(r<x||l>x)return;
		if(l==r)va(p)+=val;
		else{
			bdmd;
			UpdateP(ls(p),l,mid,x,val);
			UpdateP(rs(p),mid+1,r,x,val);
			va(p)=va(ls(p))+va(rs(p));
		}
		return;
	}
	inline ll Ask(ll p,ll l,ll r,ll le,ll ri){
		if(!p)return 0;
		if(r<le||l>ri)return 0;
		if(le<=l&&r<=ri)return va(p);
		else{
			bdmd;
			ll ans1=Ask(ls(p),l,mid,le,ri);
			ll ans2=Ask(rs(p),mid+1,r,le,ri);
			return ans1+ans2;
		}
	}
	inline ll Merge(ll p1,ll p2,ll l,ll r){
		if(!p1)return p2;
		if(!p2)return p1;
		if(l==r)va(p1)+=va(p2);
		else{
			bdmd;
			ls(p1)=Merge(ls(p1),ls(p2),l,mid);
			rs(p1)=Merge(rs(p1),rs(p2),mid+1,r);
			va(p1)=va(ls(p1))+va(rs(p1));
		}
		return p1;
	}
	#undef ls
	#undef rs
	#undef va
	#undef md
}

例題

練習題