目录
分析
线段树每种操作的实现区别的细粒度高,不好封装,并且一般在算法题中出现。
线段树代码量很大
线段树具有堆结构,一般采用静态数组实现最优,当然动态树也可以,改天更新
线段树处理动态修改查询区间非常强大,很少有别的数据结构能与之媲美
线段树进阶操作还有持久化,操作回溯,优化建图,树上二分等,这些扩展持续学习中,改日更新
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;
}