資料結構--線段樹合併

HANGRY_Sol&Cekas發表於2024-05-01

線段樹合併

前置知識

權值線段樹,動態開點線段樹

簡單說明一下,權值線段樹就是以值域開的一棵線段樹,而動態開點就是因為值域過大導致線段樹開不下,於是開一棵殘疾的線段樹。

線段樹合併模板

例:給定兩個數列 \(a , b\) , 求 \(\sum a_i+b_i\)

當然我只是為了引出模板。

程式碼 ( \(x\) 表示第一棵線段樹 , \(y\) 為後者 , 我們將 \(y\) 樹直接加到 \(x\) 上)

CODE
template <typename T> void merge(T &x , T y , T l , T r) {
    if (!x || !y) {
        x = x + y ; return x + y ; 
    }

    if (l == r) {
        ... // operation 
        return ; 
    }

    int mid = (l + r) >> 1 ; 
    merge(t[x].lson , t[y].lson , l , mid) ; 
    merge(t[x].rson , t[y].rson , mid + 1 , r) ; 
    push_up(x) ; 
}

signed main() {
    ...
    // 呼叫
    merge(Root[x] , Root[y] , 1 , Size) ; 
}

例題

Vani有約會 luogu P4556 雨天的尾巴

$$luogu$$

我們可以考慮用樹上差分,最後在一個一個加起來,易證明正確性。

Code

CODE
#include <bits/stdc++.h>
#define Lv_Jiankai Clamp
#define log_N 21
using namespace std ; 
const int N = 3e5 + 100 ; 
const int Size = 1e5 ; 
int read() {
	int x = 0 , f = 1 ; 
	char c = getchar() ; 

	while (c < '0' || c > '9') {
		if (c == '-') f = -f ; 

		c = getchar() ; 
	}

	while (c >= '0' && c <= '9') {
		x = x * 10 + c - '0' ; 
		c = getchar() ; 
	}

	return x * f ; 
}

int n , m , root ; int depth[N] ; 
int Query_father[N][21] ; int lg[N] ; int Root[N] ; 
int answer[N] ; 
class EDGE {
	public:
		int next , to ; 
} e[N] ; int head[N] , cnt , tot ; 

template <typename T> T Min(T a , T b) {
	return a < b ? a : b ; 
}

namespace SEGMENT_TREE {
	class Segment_Dynamic_Opening_Point {
		public: 
			int lson , rson , data , first_name ; 
		Segment_Dynamic_Opening_Point() {
			lson = rson = data = first_name = 0 ; 
		}
	} t[N * 80] ; int numbol = 0 ; 

	template <typename T> void push_up(T id) {
		if (t[t[id].lson].data > t[t[id].rson].data) {
			t[id].data = t[t[id].lson].data ; 
			t[id].first_name = t[t[id].lson].first_name ; 
		} else if (t[t[id].lson].data < t[t[id].rson].data) {
			t[id].data = t[t[id].rson].data ; 
			t[id].first_name = t[t[id].rson].first_name ; 
		} else {
			t[id].data = t[t[id].lson].data ; 
			t[id].first_name = Min(t[t[id].lson].first_name , t[t[id].rson].first_name) ; 
		}
	}

	template <typename T> void updata(T &id , T l , T r , T x , T v) {
		if (!id) id = ++ numbol ; 

		if (l == r) {
			t[id].data += v ; t[id].first_name = t[id].data ? l : 0 ; 
			return ; 
		}

		int mid = (l + r) >> 1 ; 
		
		if (x <= mid) {
			updata(t[id].lson , l , mid , x , v) ; 
		} else {
			updata(t[id].rson , mid + 1 , r , x , v) ; 
		}

		push_up(id) ; 
	}

	template <typename T> void merge(T &x , T y , T l , T r) {
		if (!x || !y) {
			x = x + y ; return ; 
		}

		if (l == r) {
			t[x].data += t[y].data ; 
			t[x].first_name = t[x].data ? l : 0 ; 
			return ; 
		}

		int mid = (l + r) >> 1 ; 

		merge(t[x].lson , t[y].lson , l , mid) ; 
		merge(t[x].rson , t[y].rson , mid + 1 , r) ; 
		push_up(x) ; 
	}
} using namespace SEGMENT_TREE ; 

template <typename T> void add(T x , T y) {
	cnt ++ ; 
	e[cnt].to = y ; 
	e[cnt].next = head[x] ; 
	head[x] = cnt ; 
}

template <typename T> void dfs(T x , T fa) {
	depth[x] = depth[fa] + 1 ; 
	Query_father[x][0] = fa ; 

	for (int j = 1 ; j <= lg[depth[x]] ; ++ j) {
		Query_father[x][j] = Query_father[Query_father[x][j - 1]][j - 1] ; 
	}

	for (int i = head[x] ; i ; i = e[i].next) {
		int y = e[i].to ; 

		if (y != fa) {
			dfs(y , x) ; 
		}
	}
}

template <typename T> T LCA(T x , T y) {
	if (depth[x] > depth[y]) swap(x , y) ; 

	for (int i = lg[depth[y]] ; i >= 0 ; -- i) {
		int willer = Query_father[y][i] ; 

		if (depth[willer] >= depth[x]) y = willer ; 
	}

	if (x == y) return x ; 

	for (int i = lg[depth[x]] ; i >= 0 ; -- i) {
		if (Query_father[x][i] != Query_father[y][i]) {
			x = Query_father[x][i] ; y = Query_father[y][i] ; 
		}
	}

	return Query_father[x][0] ; 
}

template <typename T> void Dfs_Query(T x , T fa) {
	for (int i = head[x] ; i ; i = e[i].next) {
		int y = e[i].to ; 

		if (y != fa) {
			Dfs_Query(y , x) ; 
			merge(Root[x] , Root[y] , 1 , Size) ; 
		}
	}

	answer[x] = t[Root[x]].first_name ; 
}

signed main() {
#ifndef ONLINE_JUDGE
	freopen("1.in" , "r" , stdin) ; 
	freopen("1.out", "w" ,stdout) ; 
#endif
	n = read() ; m = read() ; 

	for (int i = 2 ; i <= 3e5 ; ++ i) {
		lg[i] = lg[i >> 1] + 1 ; 
	}

	int x , y ; 

	for (int i = 1 ; i < n ; ++ i) {
		x = read() ; y = read() ; 
		add(x , y) ; add(y , x) ; 
	}

	root = 1 ; 
	dfs(root , 0) ; 

	int z ; 
	for (int i = 1 ; i <= m ; ++ i) {
		x = read() ; y = read() ; z = read() ; 
		int ancestor = LCA(x , y) ; 
		updata(Root[x] , 1 , Size , z , 1) ; 
		updata(Root[y] , 1 , Size , z , 1) ; 
		updata(Root[ancestor] , 1 , Size , z , -1) ; 

		if (Query_father[ancestor][0]) {
			updata(Root[Query_father[ancestor][0]] , 1 , Size , z , -1) ; 
		}
	}

	Dfs_Query(root , 0) ; 

	for (int i = 1 ; i <= n ; ++ i) {
		cout << answer[i] << '\n' ; 
	}
}

相關文章