【算法随笔:线段树 Segment tree】(多操作 | leetcode例题)

目录

分析

 DSA模板(静态数组、侵入式)

线段树经典应用

1 逆序数

2 矩形区间并


分析

线段树每种操作的实现区别的细粒度高,不好封装,并且一般在算法题中出现。

线段树代码量很大

线段树具有堆结构,一般采用静态数组实现最优,当然动态树也可以,改天更新

线段树处理动态修改查询区间非常强大,很少有别的数据结构能与之媲美

线段树进阶操作还有持久化,操作回溯,优化建图,树上二分等,这些扩展持续学习中,改日更新

 DSA模板(静态数组、侵入式)

Version I : 单点赋值 + 区间加法查询

Version II: 单点加法 + 区间加法查询

Version III:  区间赋值 + 区间加法查询

Version IV: 区间加法 + 区间最小值查询

Version V: 区间加法 + 区间最小值计数 + 区间最小值查询

一下代码,自行参考学习

/*
* 	static (instrusive and non-generic) segment tree implemented based on linear integral array
* 	generally, the DSA arc supports those operations onto an integer array:
* 		1: single point modify(assignment) [ array[index] = value ]
*		2: range modify 
			2.1: assignment[ array[first : last : 1 step] = value ]
			2.2: addition[ array[first : last : 1 step] += value ]
		3: range query 
			3.1 query by addition [ SumOf{ array[first : last : 1 step] } ]
			3.2 query be minimum [ MinOf{ array[first : last : 1 step] } ]
				3.2.0 query by minimum while recording the count [ CountOf{ MinOf{ array[first : last : 1 step] } } ]
*/

/*
*	version I
* 	only support [single point modify] and [segment query by the [addition] at certain ranges]
*/

#if defined(SINGLE_POINT_MODIFY) && defined(SEGMENT_QUERY) && defined(UPDATE_ADDITION)

#include <array>
constexpr auto __MAX__ { static_cast<int>(1e3) };
std::array<int, __MAX__ + 1> sequence {};
std::array<int, __MAX__ << 2 | 1> addition {};
constexpr int left(int index) noexcept { return x << 1; }
constexpr int right(int index) noexcept { return x << 1 | 1; }
constexpr void update(int index) noexcept {
	addition[index] = addition[left(index)] + addition[right(index)];
}

void build(int first, int last, int index) noexcept {
	if(first + 1 == last) 
		return (void) { addition[first] = sequence[first] };
	int middle { (first + last) / 2 };
	build(first, middle, left(index));
	build(middle, last, right(index));
	update(index);
}

int query(int first, int last, int current_first, int current_last, int index) noexcept {
	if(first <= current_first && last >= current_last) return addition[index];
	int middle { (current_first + current_last)/2 };
	return (
		first < middle ? query(first, last, current_first, middle, left(index)) : 0
	+	last >= middle ? query(first, last, middle, current_last, right(index)) : 0 );
}

void change(int position, int value, int first, int last, int index) noexcept {
	if(first + 1 == last) return (void) (addition[index] = v);
	int middle { (first + last)/2 };
	if(position < middle) change(position, value, first, middle, left(index));
	else change(position, value, middle, last, right(index));
	update(index);
}

#endif

/*
*	version II
* 	only support [range modify] and [segment query by the [addition] at certain ranges]
*/

#if defined(RANGE_MODIFY) && defined(SEGMENT_QUERY) && defined(UPDATE_ADDITION)

#include <array>
constexpr auto __MAX__ { static_cast<int>(1e3) };
std::array<int, __MAX__ + 1> sequence {};
std::array<int, __MAX__ << 2 | 1> addition {};
std::array<int, __MAX__ << 2 | 1> lazy {};
constexpr int left(int index) noexcept { return x << 1; }
constexpr int right(int index) noexcept { return x << 1 | 1; }
constexpr void update(int index) noexcept {
	addition[index] = addition[left(index)] + addition[right(index)];
}

void build(int first, int last, int index) noexcept {
	if(first + 1 == last) 
		return (void) { addition[first] = sequence[first] };
	int middle { (first + last) / 2 };
	build(first, middle, left(index));
	build(middle, last, right(index));
	update(index);
}

void down(int first, int last, int index) noexcept {
	int middle { (first + last) / 2 };
	if(lazy[index] > 0) {
		lazy[left(index)] = lazy[right(index)] = lazy[index];
		addition[left(index)] = (middle - first) * lazy[index];
		addition[right(index)] = (last - middle) * lazy[index];
		lazy[index] = 0;
	}
}

void change(int first, int last, int value, int current_first, int current_last, int index) noexcept {
	if(first <= current_first && last >= current_last) {
		lazy[index] = value;
		addition[index] = value * (current_last - current_first);
		return;
	}
	down(current_first, current_last, index);
	int middle { (current_first + current_last) / 2 };
	if(first < middle) change(first, last, value, current_first, middle, left(index));
	if(last >= middle) change(first, last, value, middle, current_last, right(index));
	update(index);
}

int query(int first, int last, int current_first, int current_last, int index) noexcept {
	if(first <= current_first && last >= current_last) return addition[index];
	down(current_first, current_last, index);
	int middle { (current_first + current_last) / 2 };
	return (
		first < middle ? query(first, last, current_first, middle, left(index)) : 0
	+	last >= middle ? query(first, last, middle, current_last, right(index)) : 0 );
}
#endif

/*
*	version III
* 	only support [range addition] and [segment query by the [addition] at certain ranges]
*/

#if defined(RANGE_ADDICTION) && defined(SEGMENT_QUERY) && defined(UPDATE_ADDITION)

#include <array>
constexpr auto __MAX__ { static_cast<int>(1e3) };
std::array<int, __MAX__ + 1> sequence {};
std::array<int, __MAX__ << 2 | 1> addition {};
std::array<int, __MAX__ << 2 | 1> lazy {};
constexpr int left(int index) noexcept { return x << 1; }
constexpr int right(int index) noexcept { return x << 1 | 1; }
constexpr void update(int index) noexcept {
	addition[index] = addition[left(index)] + addition[right(index)];
}

void build(int first, int last, int index) noexcept {
	if(first + 1 == last) 
		return (void) { addition[first] = sequence[first] };
	int middle { (first + last) / 2 };
	build(first, middle, left(index));
	build(middle, last, right(index));
	update(index);
}

void down(int first, int last, int index) noexcept {
	int middle { (first + last) / 2 };
	if(lazy[index]) {
		lazy[left(index)] += lazy[index];
		lazy[right(index)] += lazy[index];
		addition[left(index)] += (middle - first) * lazy[index];
		addition[right(index)] += (last - middle) * lazy[index];
		lazy[index] = 0;
	}
}

void change(int first, int last, int value, int current_first, int current_last, int index) noexcept {
	if(first <= current_first && last >= current_last) {
		lazy[index] += value;
		addition[index] += value * (current_last - current_first);
		return;
	}
	down(current_first, current_last, index);
	int middle { (current_first + current_last) / 2 };
	if(first < middle) change(first, last, value, current_first, middle, left(index));
	if(last >= middle) change(first, last, value, middle, current_last, right(index));
	update(index);
}

int query(int first, int last, int current_first, int current_last, int index) noexcept {
	if(first <= current_first && last >= current_last) return addition[index];
	down(current_first, current_last, index);
	int middle { (current_first + current_last) / 2 };
	return (
		first < middle ? query(first, last, current_first, middle, left(index)) : 0
	+	last >= middle ? query(first, last, middle, current_last, right(index)) : 0 );
}

#endif

/*
*	version IV
* 	only support [ range addition] and [segment query by the [minimum element] at certain ranges]
*/

#if defined(RANGE_ADDICTION) && defined(SEGMENT_QUERY) && defined(UPDATE_MINIMUM)

#include <array>
#include <algorithm>

constexpr auto __MAX__ { static_cast<int>(1e3) };
std::array<int, __MAX__ + 1> sequence {};
std::array<int, __MAX__ << 2 | 1> minimum {};
std::array<int, __MAX__ << 2 | 1> lazy {};
constexpr int left(int index) noexcept { return x << 1; }
constexpr int right(int index) noexcept { return x << 1 | 1; }
constexpr void update(int index) noexcept {
	minimum[index] = std::min(minimum[left(index)], minimum[right(index)]);
}

void build(int first, int last, int index) noexcept {
	if(first + 1 == last) 
		return (void) { addition[first] = sequence[first] };
	int middle { (first + last) / 2 };
	build(first, middle, left(index));
	build(middle, last, right(index));
	update(index);
}

void down(int first, int last, int index) noexcept {
	int middle { (first + last) / 2 };
	if(lazy[index]) {
		lazy[left(index)] += lazy[index];
		lazy[right(index)] += lazy[index];
		minimum[left(index)] += lazy[index];
		minimum[right(index)] += lazy[index];
		lazy[index] = 0;
	}
}

void change(int first, int last, int value, int current_first, int current_last, int index) noexcept {
	if(first <= current_first && last >= current_last) {
		lazy[index] += value;
		minimum[index] += value;
		return;
	}
	down(current_first, current_last, index);
	int middle { (current_first + current_last) / 2 };
	if(first < middle) change(first, last, value, current_first, middle, left(index));
	if(last >= middle) change(first, last, value, middle, current_last, right(index));
	update(index);
}

int query(int first, int last, int current_first, int current_last, int index) noexcept {
	if(first <= current_first && last >= current_last) return minimum[index];
	down(current_first, current_last, index);
	int middle { (current_first + current_last) / 2 };
	return (std::min({
		first < middle ? query(first, last, current_first, middle, left(index)) : 0,
	last >= middle ? query(first, last, middle, current_last, right(index)) : 0 }));
}

#endif

/*
*	version V
* 	only support [range addition] and [segment query by the [minimum element && the number of minimum] at certain ranges]
*/

#if defined(RANGE_ADDICTION) && defined(SEGMENT_QUERY) && defined(UPDATE_MINIMUM) && defined(UPDATE_COUNTER)

#include <array>
#include <algorithm>
#include <limits>

constexpr auto __MAX__ { static_cast<int>(1e3) };
std::array<int, __MAX__ + 1> sequence {};
std::array<int, __MAX__ << 2 | 1> minimum {};
std::array<int, __MAX__ << 2 | 1> counter {};
std::array<int, __MAX__ << 2 | 1> lazy {};
constexpr int left(int index) noexcept { return x << 1; }
constexpr int right(int index) noexcept { return x << 1 | 1; }
constexpr void update(int index) noexcept {
	minimum[index] = std::min(minimum[left(index)], minimum[right(index)]);
	if(minimum[left(index)] == minimum[right(index)]) 
		return (void) (counter[index] = counter[left(index)] + counter[right(index)]);
	return (void) (counter[index] = minimum[left(index)] < minimum[right(index)] ?
					counter[left(index)] : counter[right(index)]);
}

void build(int first, int last, int index) noexcept {
	if(first + 1 == last) 
		return (void) { addition[first] = sequence[first] };
	int middle { (first + last) / 2 };
	build(first, middle, left(index));
	build(middle, last, right(index));
	update(index);
}

void down(int first, int last, int index) noexcept {
	int middle { (first + last) / 2 };
	if(lazy[index]) {
		lazy[left(index)] += lazy[index];
		lazy[right(index)] += lazy[index];
		minimum[left(index)] += lazy[index];
		minimum[right(index)] += lazy[index];
		lazy[index] = 0;
	}
}

void change(int first, int last, int value, int current_first, int current_last, int index) noexcept {
	if(first <= current_first && last >= current_last) {
		lazy[index] += value;
		minimum[index] += value;
		return;
	}
	down(current_first, current_last, index);
	int middle { (current_first + current_last) / 2 };
	if(first < middle) change(first, last, value, current_first, middle, left(index));
	if(last >= middle) change(first, last, value, middle, current_last, right(index));
	update(index);
}

std::pair<int, int> query(int first, int last, int current_first, int current_last, int index) noexcept {
	if(first <= current_first && last >= current_last) return {minimum[index], counter[index]};
	down(current_first, current_last, index);
	int middle { (current_first + current_last) / 2 };
	std::pair<int, int> result { std::numeric_limits<int>::max(), 0 };
	if(first < middle) result = std::min(result, query(first, last, current_first, middle, left(index));
	if(last >= middle) {
		auto temporary { std::min(result, query(first, last, middle, current_last, right(index)) };
		if(temporary.first == result.first) result.second += temporary.second;
		else if(temporary.first < result.first) result = std::move(temporary);
	}
	return result;
}

#endif



线段树经典应用

1 逆序数

/*
* 	给定一个长度为N的序列 {sequence}
*	求出这个序列所包含的所有逆序对
* 	range:	max {N} == 1e5;
*			max {sequence[i]} == INT_MAX
*			max {uniques {sequence}} == 1e3 
*/

#include <utility>
#include <vector>
#include <algorithm>
#include <array>
#include <cstdio>

constexpr __MAX__ { static_cast<int>(1e5) };
constexpr __UNIQ__ { static_cast<int>(1e3) };
std::array<int, __MAX__ + 1> sequence {};
std::array<int, __MAX__ + 1> shrinked {};
std::array<int, __UNIQ__ << 2 | 1> addition {};

int N {};
int result {};

constexpr int left(int index) noexcept { return x << 1; }
constexpr int right(int index) noexcept { return x << 1 | 1; }
constexpr void update(int index) noexcept {
	addition[index] = addition[left(index)] + addition[right(index)];
}

void build(int first, int last, int index) noexcept {
	if(first + 1 == last) 
		return (void) { addition[first] = sequence[first] };
	int middle { (first + last) / 2 };
	build(first, middle, left(index));
	build(middle, last, right(index));
	update(index);
}

int query(int first, int last, int current_first, int current_last, int index) noexcept {
	if(first <= current_first && last >= current_last) return addition[index];
	int middle { (current_first + current_last)/2 };
	return (
		first < middle ? query(first, last, current_first, middle, left(index)) : 0
	+	last >= middle ? query(first, last, middle, current_last, right(index)) : 0 );
}

void change(int position, int value, int first, int last, int index) noexcept {
	if(first + 1 == last) return (void) (addition[index] += v);
	int middle { (first + last)/2 };
	if(position < middle) change(position, value, first, middle, left(index));
	else change(position, value, middle, last, right(index));
	update(index);
}

int main(void) {
	std::scanf("%d", &N);
	for(int i {}; i < N; (void) (shrinked[i] = sequence[i]), (void) i ++) 
		std::scanf("%d", &sequence[i]);
	std::sort(shrinked.begin(), shrinked.begin() + N);
	auto uniques { 
		static_cast<int>(
			std::distance(shrinked.begin(), std::unique(shrinked.begin(), shrinked.begin() + N))) };
	for(int i {}; i < N; ++i) 
		sequence[i] = static_cast<int>(
			std::distance(std::lower_bound(shrinked.begin(), shrinked.begin() + uniques, sequence[i]), shrinked.begin()));
	build(0, N, 0);
	for(int i {}; i < uniques; ++i) {
		if(sequence[i] == uniques - 1) continue;
		result += query(sequence[i] + 1, uniques, 0, uniques, 0);
		change(sequence[i] + 1, 1, 0, uniques, 0);
	}
	std::printf("%d\n", result);
	return 0;
} 



2 矩形区间并

#include <algorithm>
#include <array>
#include <vector>
#include <utility>
#include <functional>
#include <cstdio>
#include <limits>

constexpr auto __MAX__ { static_cast<int>(1e5) };

constexpr int left(int index) noexcept { return x << 1; }
constexpr int right(int index) noexcept { return x << 1 | 1; }

int retangle_counter {};
int area_union {};

std::array<int, __MAX__ + 1> x_unique {};
int x_unique_counter {};
std::array<int, __MAX__ + 1> y_unique {};
int y_unique_counter {};

struct rectangle{
	int x1 {};
	int x2 {};
	int y1 {};
	int y2 {};
} rectangles[__MAX__];

struct event {
	int first {};
	int last {};
	int modifier {};
};

std::array<std::vector<event>, __MAX__ + 1> events {};

std::array<int, __MAX__ << 2 | 1> minimum {};
std::array<int, __MAX__ << 2 | 1> counter {};
std::array<int, __MAX__ << 2 | 1> lazy {};

constexpr int left(int index) noexcept { return x << 1; }
constexpr int right(int index) noexcept { return x << 1 | 1; }

constexpr void update(int index) noexcept {
	minimum[index] = std::min(minimum[left(index)], minimum[right(index)]);
	if(minimum[left(index)] == minimum[right(index)]) 
		return (void) (counter[index] = counter[left(index)] + counter[right(index)]);
	return (void) (counter[index] = minimum[left(index)] < minimum[right(index)] ?
					counter[left(index)] : counter[right(index)]);
}

void build(int first, int last, int index) noexcept {
	if(first + 1 == last) 
		return (void) ( addition[first] =  std::minis(x_unique[first + 1] - x_unique[first]) );
	int middle { (first + last) / 2 };
	build(first, middle, left(index));
	build(middle, last, right(index));
	update(index);
}

void down(int first, int last, int index) noexcept {
	int middle { (first + last) / 2 };
	if(lazy[index]) {
		lazy[left(index)] += lazy[index];
		lazy[right(index)] += lazy[index];
		minimum[left(index)] += lazy[index];
		minimum[right(index)] += lazy[index];
		lazy[index] = 0;
	}
}

void change(int first, int last, int value, int current_first, int current_last, int index) noexcept {
	if(first <= current_first && last >= current_last) {
		lazy[index] += value;
		minimum[index] += value;
		return;
	}
	down(current_first, current_last, index);
	int middle { (current_first + current_last) / 2 };
	if(first < middle) change(first, last, value, current_first, middle, left(index));
	if(last >= middle) change(first, last, value, middle, current_last, right(index));
	update(index);
}

std::pair<int, int> query(int first, int last, int current_first, int current_last, int index) noexcept {
	if(first <= current_first && last >= current_last) return {minimum[index], counter[index]};
	down(current_first, current_last, index);
	int middle { (current_first + current_last) / 2 };
	std::pair<int, int> result { std::numeric_limits<int>::max(), 0 };
	if(first < middle) result = std::min(result, query(first, last, current_first, middle, left(index));
	if(last >= middle) {
		auto temporary { std::min(result, query(first, last, middle, current_last, right(index)) };
		if(temporary.first == result.first) result.second += temporary.second;
		else if(temporary.first < result.first) result = std::move(temporary);
	}
	return result;
}

int main(void) {
	std::scanf("%d", &retangle_counter);
	for(int i {}; i < retangle_counter; ++i) {
		std::scanf("%d%d%d%d",
			&rectangles[i].x1,
			&rectangles[i].x2,
			&rectangles[i].y1,
			&rectangles[i].y2);
		x_unique[x_unique_counter ++] = rectangles[i].x1;
		x_unique[x_unique_counter ++] = rectangles[i].x2;
		y_unique[y_unique_counter ++] = rectangles[i].y1;
		y_unique[y_unique_counter ++] = rectangles[i].y2;
	}
	std::sort(x_unique.begin(), x_unique.begin() + x_unique_counter);
	std::sort(y_unique.begin(), y_unique_begin() + y_unique_counter);
	auto x_shrink_counter { 
		static_cast<int>(
			std::distance(std::unique(x_unique.begin(), x_unique.begin() + x_unique_counter), x_unique.begin())) };
	auto y_shrink_counter { 
		static_cast<int>(
			std::distance(std::unique(y_unique.begin(), y_unique.begin() + y_unique_counter), y_unique.begin())) };
	for(int i {}; i < rectangle_counter; ++i) {
		auto x1_shrink { static_cast<int>(
			std::distance(std::lower_bound(x_unique.begin(), x_unique.begin() + x_shrink_counter, rectangles[i].x1), x_unique.begin())) };
		auto x2_shrink { static_cast<int>(
			std::distance(std::lower_bound(x_unique.begin(), x_unique.begin() + x_shrink_counter, rectangles[i].x2), x_unique.begin())) };
		auto y1_shrink { static_cast<int>(
			std::distance(std::lower_bound(y_unique.begin(), y_unique.begin() + y_shrink_counter, rectangles[i].y1), y_unique.begin())) };
		auto y2_shrink { static_cast<int>(
			std::distance(std::lower_bound(y_unique.begin(), y_unique.begin() + y_shrink_counter, rectangles[i].y2), y_unique.begin())) };
		if(x1 == x2) continue;
		events[y1].emplace_back(x1, x2 + 1, 1);
		events[y2].emplace_back(x1, x2 + 1, -1);
	}
	x_unique[x_shrink_counter] = x_unique[x_shrink_counter - 1];
	y_unique[y_shrink_counter] = y_unique[y_shrink_counter - 1];
	build(0, x_shrink_counter, 0);
	for(int i {}; i < y_shrink_counter; ++i) {
		int temporary { counter[i] };
		if(minimum[i] > 0) temporary = 0;
		area_union += (y_unique[i] - y_unique[i + 1]) * (x_unique[x_shrink_counter] - x_unique[0] - temporary);
		for(auto [first, last, modifier] : events[i]) 
			change(first, last, modifier, 0, x_shrink_counter, 0); 
	}
	std::printf("%d\n", area_union);
	return 0;
}







  • 23
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

XNB's Not a Beginner

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值