回到算法筆記-進階資料結構

線段樹

Segment Tree是一種二元樹,適合解決RMQ問題(查詢區間極值)、區間和, 適合用於單點修改、區間修改。
成功實現Sparse Table做不到的事。

每個節點可以存一種區間的數據(比如區間和),
根節點存[1,n]的數據,每個左子節點編號是index*2,右邊為index*2+1,
左邊紀錄[l,(l+r)/2),右邊紀錄[(l+r)/2+1,r]。

通常,線段樹會開4倍大

int T[4*N],a[N]; // int T[N<<2];

建立線段樹,若l==r,此節點即a[l]

子樹建完後就把資料合併

void build(int l,int r,int index){
	if(l==r){
		T[l] = a[l];
	}
	else{
		int mid = l+(r-l)/2; // 避免 (l+r)/2 溢位 
		build(index*2,l,mid); // build(index<<1,l,mid);
		build(index*2+1,mid+1,r);
		T[index] = T[index*2] + T[index*2+1] ;
	}
}

詢問區間(ql,qr:要查的區間 l,r:此節點儲存的左右界)

int query(int ql,int qr,int l,int r,int index){
	if(ql<=l && qr>=r) return T[index];
	int mid = l+(r-l)/2;
	int rt=0;
	if(ql>mid) rt+=query(ql,qr,mid+1,r,index*2+1);
	if(qr<=mid) rt+=query(ql,qr,l,mid,index*2);
	return rt;
}

單點修改

void modify(int l,int r,int pos,int index,int val){
	if(l==r){
		T[index] = val;return;
	}
	int mid = l+(r-l)/2;
	if(pos<=mid) modify(l,mid,pos,index*2,val);
	else modify(mid+1,r,pos,index*2+1,val);
	T[index] = T[index*2]+T[index*2+1];
}

懶人標記

如果想在[l,r]中的所有元素加值怎麼辦? 不可能一個一個呼叫modify吧。

我們可以先在要加的區間內貼標籤,最後要查詢時再加上去就好

  • 這裡用了更多變數,建議改用結構體來實作可以減少程式碼
void push(int l,int r,int idx){
	Tag[2*idx] += Tag[idx]; // 左子節點貼標籤
	Tag[2*idx+1] += Tag[idx]; // 右子節點貼標籤
	T[idx] = T[idx] + Tag[idx] * (r-l+1); // 最後的答案
	Tag[idx] = 0;
}
void modify(int ql,int qr,int l,int r,int index,int val){
	if(ql<=l&&r>=qr){
		Tag[index] += val; // 在這個節點貼上標籤
		return;
	}
	int mid = l+(r-l)/2;
	if(ql>mid) modify(ql,qr,mid+1,r,idx*2+1,val);
	else if(qr<=mid) modify(ql,qr,l,mid,idx*2,val);
	else{
		modify(ql,qr,l,mid,idx*2);
		modify(ql,qr,mid+1,r,idx*2+1);
	}
	T[idx] = T[idx*2] + Tag[idx*2] * (mid-l+1) + T[idx*2+1] + Tag[idx*2+1] * (r-mid);
}
int query(int ql,int qr,int l,int r,int idx){
	if(ql<=l&&r<=qr) return T[idx] + Tag[idx]*(r-l+1);
	push(l,r,idx);
	int mid = l+(r-l)/2;
	
	if(ql>mid) return query(ql,qr,mid+1,r,idx*2+1);
	else if(qr<=mid) return query(ql,qr,l,mid,idx*2);
	else return query(ql,qr,l,mid,idx*2) + query(ql,qr,mid+1,r,idx*2+1);
}

練習:

ZeroJudge d539
ZeroJudge d668
ZeroJudge d799