1. 程式人生 > 其它 >【演算法筆記】動態開點,線段樹合併和可持久化線段樹

【演算法筆記】動態開點,線段樹合併和可持久化線段樹

前言

這兩個東西應該是很重要的基礎,特別是對於權值線段樹和主席樹。

因為不想篇幅太長就單獨提出來寫了。

建議是和權值線段樹,主席樹那一篇配合食用。

動態開點

你知道正常的線段樹一般都是用二倍標記法去標記兒子的序號。

然後線上段樹那一篇裡面我證過,這樣子至少需要四倍空間才不會出現RE。

但在某些特殊的情況下(比如權值線段樹),你需要維護的範圍可能非常大,如果真的要全部建樹建出來,可能空間就爆了。

然後這個時候利用一個類似線段樹懶標記的思想,如果一個節點需要使用,那麼我們才建立這個節點,反之就不用。

從實現上來講,就是初始的時候只建立一個根節點代表整個區間。

然後當查詢或者修改某個區間的時候我們才向下遞迴建樹。

而且不同於原來的二倍標記法,現在是以直接記錄每一個節點的左右兒子的編號來建立線段樹。

我寫的正常線段樹是把一個節點的區間資訊直接加到了節點的資訊當中。

在這裡就直接作為函式的引數傳遞了。

這裡用一份單點修改,並且維護區間和的程式碼來寫一下。


#include<bits/stdc++.h>
using namespace std;

#define lson(o) t[o].ls
#define rson(o) t[o].rs
#define sum(o) t[o].sum
const int si=1e5+10;
struct Segment_Tree{
	int ls=0,rs=0;
	int sum=0;// 注意這裡要先初始化
}t[si<<1];

int tot=0,root=0;
void change(int &p,int l,int r,int x,int v){
	if(!p) p=++tot; // 動態開點,注意此處使用了 &p
	if(l==r){
		sum(p)+=v;
		return;
	}
	int mid=(l+r)>>1;
	if(x<=mid) change(lson(p),l,mid,x,v);
	else change(rson(p),mid+1,r,x,v);
	sum(p)=sum(lson(p))+sum(rson(p)); //Pushup
}

int main(){
	//......
	change(root,1,n,x,v);
}

線段樹合併

有的時候,你可能會對於一個值域建立多個根節點,而且每一次操作在不同的幾棵動態開點線段樹上進行,那麼到最後所有操作完成你需要查詢的時候。

就可以使用線段樹合併來解決。

簡單來說,我們需要把每一顆線段樹合併起來。

因為維護的值域相同,那麼顯然它們對於值域的子區間的劃分也是一樣。

所以我們可以同時從兩顆線段樹的樹根開始,用 \(p,q\) 兩個指標同步向下進行遍歷。

也就是說每一步的時候 \(p,q\) 分別代表兩顆線段樹上的劃分相同的兩個節點(說白了就是假設把這個值域用普通線段樹建出來之後,\(p,q\) 對應的是同一個節點)。

遍歷的時候可能會出現這四種情況:

  1. \(p\) 沒有建立節點,而 \(q\)
    建立有節點。
  2. \(q\) 沒有建立節點,而 \(p\) 建立有節點。
  3. \(p,q\) 都有建立這個節點。
  4. \(p,q\) 都沒有建立這個節點。

對於 \(1,2\) ,我們就直接把非空節點作為合併後的節點。

對於 \(3\) ,既然你倆都沒有,那合併後也沒必要要這個點,也就是合併後仍舊沒有這個節點。

對於 \(4\) ,既然你們都有,那就合併你們的資訊(類似於普通線段樹的Pushup),然後隨便找一個節點作為合併之後的節點(一般是 \(p\)

這個的程式碼如下:


#include<bits/stdc++.h>
using namespace std;

#define lson(o) t[o].ls
#define rson(o) t[o].rs
#define sum(o) t[o].sum

const int si=1e5+10;
struct Segment_Tree{
	int ls=0,rs=0;
	int sum=0;
}t[si<<5];

int merge(int p,int q,int l,int r){
	if(!p) return q;
	if(!q) return p;
	if(l==r){
		sum(p)+=sum(q);
		return p;
	}
	int mid=(l+r)>>1;
	lson(p)=merge(lson(p),lson(q),l,mid);
	rson(p)=merge(rson(p),rson(q),mid+1,r);
	sum(p)=sum(lson(p))+sum(rson(p));
	return p;
}

int main(){
	//....
}

可持久化線段樹

None

本文來自部落格園,作者:black_trees,轉載請註明原文連結:https://www.cnblogs.com/BTeqwq/p/sta-merge-fe.html