Skip to content

Commit 3b4cb20

Browse files
committed
feat: Implement Segment Tree data structure with point update and range query capabilities
1 parent c1ff9fb commit 3b4cb20

File tree

2 files changed

+241
-0
lines changed

2 files changed

+241
-0
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#define PROBLEM "https://judge.yosupo.jp/problem/point_set_range_composite"
2+
3+
#include "../weilycoder/ds/segment_tree.hpp"
4+
#include <cstdint>
5+
#include <iostream>
6+
#include <vector>
7+
using namespace std;
8+
using namespace weilycoder;
9+
10+
static constexpr uint64_t mod = 998244353;
11+
12+
struct AffineMonoid {
13+
using value_type = pair<uint64_t, uint64_t>; // (a, b) represents f(x) = a * x + b
14+
15+
static value_type identity() { return {1, 0}; }
16+
static value_type operation(const value_type &f, const value_type &g) {
17+
return {(g.first * f.first) % mod, (g.first * f.second + g.second) % mod};
18+
}
19+
20+
static uint64_t affine(const value_type &f, uint64_t x) {
21+
return (f.first * x + f.second) % mod;
22+
}
23+
};
24+
25+
int main() {
26+
cin.tie(nullptr)->sync_with_stdio(false);
27+
cin.exceptions(cin.badbit | cin.failbit);
28+
size_t n, q;
29+
cin >> n >> q;
30+
31+
vector<pair<uint64_t, uint64_t>> affines;
32+
affines.reserve(n);
33+
for (size_t i = 0; i < n; ++i) {
34+
uint64_t a, b;
35+
cin >> a >> b;
36+
affines.emplace_back(a, b);
37+
}
38+
39+
SegmentTree<AffineMonoid> sgt(affines);
40+
while (q--) {
41+
size_t op;
42+
cin >> op;
43+
if (op == 0) {
44+
size_t p;
45+
uint64_t c, d;
46+
cin >> p >> c >> d;
47+
sgt.point_set(p, {c, d});
48+
} else {
49+
size_t l, r;
50+
uint64_t x;
51+
cin >> l >> r >> x;
52+
auto func = sgt.range_query(l, r);
53+
cout << AffineMonoid::affine(func, x) << '\n';
54+
}
55+
}
56+
return 0;
57+
}

weilycoder/ds/segment_tree.hpp

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
#ifndef WEILYCODER_SEGMENT_TREE_HPP
2+
#define WEILYCODER_SEGMENT_TREE_HPP
3+
4+
/**
5+
* @file segment_tree.hpp
6+
* @brief Segment Tree Data Structure
7+
*/
8+
9+
#include <cstddef>
10+
#include <limits>
11+
#include <stdexcept>
12+
#include <vector>
13+
14+
namespace weilycoder {
15+
/**
16+
* @brief Segment Tree (point update and range query)
17+
* @tparam Monoid The monoid defining the operation and identity
18+
* @tparam ptr_t The type used for indexing (default: size_t)
19+
*/
20+
template <typename Monoid, typename ptr_t = size_t> struct SegmentTree {
21+
using T = typename Monoid::value_type;
22+
static constexpr ptr_t null = std::numeric_limits<ptr_t>::max();
23+
24+
/**
25+
* @brief Node structure for the segment tree
26+
*/
27+
struct Node {
28+
T value;
29+
ptr_t left, right;
30+
31+
Node() : value(Monoid::identity()), left(null), right(null) {}
32+
};
33+
34+
private:
35+
ptr_t tl, tr;
36+
std::vector<Node> data;
37+
38+
void pushup(size_t node) {
39+
data[node].value =
40+
Monoid::operation(data[data[node].left].value, data[data[node].right].value);
41+
}
42+
43+
ptr_t build(ptr_t l, ptr_t r) {
44+
ptr_t node = data.size();
45+
data.emplace_back();
46+
47+
if (r - l > 1) {
48+
ptr_t mid = l + ((r - l) >> 1);
49+
ptr_t left = build(l, mid), right = build(mid, r);
50+
data[node].left = left, data[node].right = right;
51+
}
52+
53+
return node;
54+
}
55+
56+
void init(ptr_t node, ptr_t l, ptr_t r, const std::vector<T> &arr) {
57+
if (r - l == 1)
58+
data[node].value = arr[l];
59+
else {
60+
ptr_t mid = l + ((r - l) >> 1);
61+
init(data[node].left, l, mid, arr);
62+
init(data[node].right, mid, r, arr);
63+
pushup(node);
64+
}
65+
}
66+
67+
void point_set(ptr_t node, ptr_t l, ptr_t r, ptr_t pos, const T &val) {
68+
if (r - l == 1)
69+
data[node].value = val;
70+
else {
71+
ptr_t mid = l + ((r - l) >> 1);
72+
if (pos < mid)
73+
point_set(data[node].left, l, mid, pos, val);
74+
else
75+
point_set(data[node].right, mid, r, pos, val);
76+
pushup(node);
77+
}
78+
}
79+
80+
void point_update(ptr_t node, ptr_t l, ptr_t r, ptr_t pos, const T &val) {
81+
if (r - l == 1)
82+
data[node].value = Monoid::operation(data[node].value, val);
83+
else {
84+
ptr_t mid = l + ((r - l) >> 1);
85+
if (pos < mid)
86+
point_update(data[node].left, l, mid, pos, val);
87+
else
88+
point_update(data[node].right, mid, r, pos, val);
89+
pushup(node);
90+
}
91+
}
92+
93+
T point_query(ptr_t node, ptr_t l, ptr_t r, ptr_t pos) const {
94+
if (r - l == 1)
95+
return data[node].value;
96+
ptr_t mid = l + ((r - l) >> 1);
97+
if (pos < mid)
98+
return point_query(data[node].left, l, mid, pos);
99+
else
100+
return point_query(data[node].right, mid, r, pos);
101+
}
102+
103+
T range_query(ptr_t node, ptr_t l, ptr_t r, ptr_t ql, ptr_t qr) const {
104+
if (ql >= r || qr <= l)
105+
return Monoid::identity();
106+
if (ql <= l && r <= qr)
107+
return data[node].value;
108+
ptr_t mid = l + ((r - l) >> 1);
109+
T left_res = range_query(data[node].left, l, mid, ql, qr);
110+
T right_res = range_query(data[node].right, mid, r, ql, qr);
111+
return Monoid::operation(left_res, right_res);
112+
}
113+
114+
public:
115+
/**
116+
* @brief Constructs a SegmentTree with given size
117+
* @param size The size of the array
118+
*/
119+
explicit SegmentTree(ptr_t size) : tl(0), tr(size) { build(tl, tr); }
120+
121+
/**
122+
* @brief Constructs a SegmentTree for the range [left, right)
123+
* @param left The left index (inclusive)
124+
* @param right The right index (exclusive)
125+
*/
126+
explicit SegmentTree(ptr_t left, ptr_t right) : tl(left), tr(right) { build(tl, tr); }
127+
128+
/**
129+
* @brief Constructs a SegmentTree from an initial array
130+
* @param arr Initial array of elements
131+
*/
132+
explicit SegmentTree(const std::vector<T> &arr)
133+
: tl(0), tr(static_cast<ptr_t>(arr.size())) {
134+
build(tl, tr), init(0, tl, tr, arr);
135+
}
136+
137+
/**
138+
* @brief Sets the value at position pos to val
139+
* @param pos The position to update
140+
* @param val The new value
141+
*/
142+
void point_set(ptr_t pos, const T &val) {
143+
if (pos < tl || pos >= tr)
144+
throw std::out_of_range("SegmentTree::point_set: position out of range");
145+
point_set(0, tl, tr, pos, val);
146+
}
147+
148+
/**
149+
* @brief Updates the value at position pos by applying the monoid operation with val
150+
* @param pos The position to update
151+
* @param val The value to combine
152+
*/
153+
void point_update(ptr_t pos, const T &val) {
154+
if (pos < tl || pos >= tr)
155+
throw std::out_of_range("SegmentTree::point_update: position out of range");
156+
point_update(0, tl, tr, pos, val);
157+
}
158+
159+
/**
160+
* @brief Queries the value at position pos
161+
* @param pos The position to query
162+
* @return The value at position pos
163+
*/
164+
T point_query(ptr_t pos) const {
165+
if (pos < tl || pos >= tr)
166+
throw std::out_of_range("SegmentTree::point_query: position out of range");
167+
return point_query(0, tl, tr, pos);
168+
}
169+
170+
/**
171+
* @brief Queries the range [left, right)
172+
* @param left The left index (inclusive)
173+
* @param right The right index (exclusive)
174+
* @return The result of the monoid operation over the range
175+
*/
176+
T range_query(ptr_t left, ptr_t right) const {
177+
if (left < tl || right > tr || left > right)
178+
throw std::out_of_range("SegmentTree::range_query: range out of bounds");
179+
return range_query(0, tl, tr, left, right);
180+
}
181+
};
182+
} // namespace weilycoder
183+
184+
#endif

0 commit comments

Comments
 (0)