// https://www.infoarena.ro/problema/arbint
#include <string>
#pragma once
#include <fstream>
#include <iostream>
#include <vector>
#include <filesystem>
template<typename T>
std::istream& operator>>(std::istream &in, std::vector<T> &vec) {
for (auto &x : vec) {
in >> x;
}
return in;
}
template<typename T>
std::ostream& operator<<(std::ostream &out, const std::vector<T> &vec) {
for (const auto &x : vec) {
out << x << " ";
}
out << "\n";
return out;
}
class Reader {
template<typename T>
friend Reader& operator>>(Reader &reader, T& t) {
(*reader.in) >> t;
return reader;
}
protected:
explicit Reader (std::istream *in = nullptr) : in(in) {}
std::istream *in;
};
class FileReader : public Reader {
public:
explicit FileReader(const std::string &filename) : fin(filename) {
in = &fin;
}
private:
std::ifstream fin;
};
class StdinReader : public Reader {
public:
StdinReader() : Reader(&std::cin) {}
};
#ifdef LOCAL_PROJECT
std::string input_path() {
std::string file = __FILE__;
std::string directory = file.substr(0, file.rfind('/'));
return directory + "/input.txt";
}
FileReader cin(input_path());
#else
StdinReader cin;
#endif
#pragma once
#include <optional>
#include <cassert>
#include <memory>
template<typename Data, typename Operation, template<typename> typename Allocator = std::allocator>
class SegmentTree {
public:
explicit SegmentTree(size_t n);
template<typename T>
explicit SegmentTree(const std::vector<T> &v);
template<typename ...Args>
void update(size_t left, size_t right, Args &&...args);
Data query(size_t left, size_t right);
private:
struct Node {
Node() = default;
explicit Node(const Data &data, Node *leftSon = nullptr, Node *rightSon = nullptr) :
data(data), leftSon(leftSon), rightSon(rightSon) {}
Data data;
Operation lazy;
Node *leftSon, *rightSon;
};
template<typename ...Args>
Node *makeNode(Args &&...args);
template<typename T>
Node *build(const std::vector<T> &v);
template<typename T>
Node *build(const std::vector<T> &v, size_t left, size_t right);
static size_t getMiddle(size_t left, size_t right);
void updateImpl(Node *node, size_t left, size_t right, size_t from, size_t to, const Operation &op);
Data queryImpl(Node *node, size_t left, size_t right, size_t from, size_t to);
void push(Node *node, size_t left, size_t right);
bool apply(const Operation &op, Node *node, size_t left, size_t right);
Allocator<Node> alloc;
size_t n;
Node *root;
};
template<typename T>
struct SumData {
SumData() : sum(0) {}
template<typename U>
explicit SumData(const U &u) : sum(u) {}
SumData(const SumData &left, const SumData &right) : sum(left.sum + right.sum) {}
T sum;
};
template<typename T>
struct MaxData {
MaxData() : max(std::numeric_limits<T>::min()) {}
template<typename U>
explicit MaxData(const U &u) : max(u) {}
MaxData(const MaxData &left, const MaxData &right) : max(std::max(left.max, right.max)) {}
T max;
};
template<typename T>
struct MinData {
MinData() : min(std::numeric_limits<T>::max()) {}
template<typename U>
explicit MinData(const U &u) : min(u) {}
MinData(const MinData &left, const MinData &right) : min(std::min(left.min, right.min)) {}
T min;
};
template<typename T>
struct SetOperation {
SetOperation() = default;
explicit SetOperation(const T &t) : val(t) {}
SetOperation(const SetOperation &op1, const SetOperation &op2) : val(op2.val) {}
bool apply(SumData<T> &data, size_t left, size_t right) const {
if (val) {
data.sum = *val * (right - left + 1);
}
return true;
}
bool apply(MaxData<T> &data, size_t, size_t) const {
if (val) {
data.max = *val;
}
return true;
}
bool apply(MinData<T> &data, size_t, size_t) const {
if (val) {
data.min = *val;
}
return true;
}
std::optional<T> val;
};
template<typename T>
struct AddOperation {
AddOperation() : add(0) {}
explicit AddOperation(const T &t) : add(t) {}
AddOperation(const AddOperation &op1, const AddOperation &op2) : add(op1.add + op2.add) {}
bool apply(SumData<T> &data, size_t left, size_t right) const {
data.sum += add * (right - left + 1);
return true;
}
bool apply(MaxData<T> &data, size_t, size_t) const {
data.max += add;
return true;
}
bool apply(MinData<T> &data, size_t, size_t) const {
data.min += add;
return true;
}
T add;
};
/* ----------------------------------------------IMPLEMENTATION------------------------------------------------------ */
template<typename Data, typename Operation, template<typename> typename Allocator>
SegmentTree<Data, Operation, Allocator>::SegmentTree(size_t n) : n(n), root(makeNode()) {}
template<typename Data, typename Operation, template<typename> typename Allocator>
template<typename T>
SegmentTree<Data, Operation, Allocator>::SegmentTree(const std::vector<T> &v) : n(v.size()), root(build(v)) {}
template<typename Data, typename Operation, template<typename> typename Allocator>
template<typename... Args>
void SegmentTree<Data, Operation, Allocator>::update(size_t left, size_t right, Args &&... args) {
assert(left >= 0 && right < n);
if (left > right) {
return;
}
Operation op(std::forward<Args>(args)...);
updateImpl(root, 0, n - 1, left, right, op);
}
template<typename Data, typename Operation, template<typename> typename Allocator>
Data SegmentTree<Data, Operation, Allocator>::query(size_t left, size_t right) {
assert(left >= 0 && right < n);
if (left > right) {
return Data();
}
return queryImpl(root, 0, n - 1, left, right);
}
template<typename Data, typename Operation, template<typename> typename Allocator>
template<typename ...Args>
SegmentTree<Data, Operation, Allocator>::Node *SegmentTree<Data, Operation, Allocator>::makeNode(Args &&...args) {
Node *ptr = alloc.allocate(1);
std::construct_at(ptr, std::forward<Args>(args)...);
return ptr;
}
template<typename Data, typename Operation, template<typename> typename Allocator>
template<typename T>
SegmentTree<Data, Operation, Allocator>::Node *SegmentTree<Data, Operation, Allocator>::build(const std::vector<T> &v) {
return build(v, 0, v.size() - 1);
}
template<typename Data, typename Operation, template<typename> typename Allocator>
template<typename T>
SegmentTree<Data, Operation, Allocator>::Node *
SegmentTree<Data, Operation, Allocator>::build(const std::vector<T> &v, size_t left, size_t right) {
if (left == right) {
return makeNode(Data(v[left]));
} else {
size_t middle = getMiddle(left, right);
auto leftSon = build(v, left, middle), rightSon = build(v, middle + 1, right);
return makeNode(Data(leftSon->data, rightSon->data), leftSon, rightSon);
}
}
template<typename Data, typename Operation, template<typename> typename Allocator>
size_t SegmentTree<Data, Operation, Allocator>::getMiddle(size_t left, size_t right) {
return left + (right - left) / 2;
}
template<typename Data, typename Operation, template<typename> typename Allocator>
void SegmentTree<Data, Operation, Allocator>::push(SegmentTree::Node *node, size_t left, size_t right) {
if (left != right) {
if (!node->leftSon) { node->leftSon = makeNode(); }
if (!node->rightSon) { node->rightSon = makeNode(); }
auto middle = getMiddle(left, right);
assert(apply(node->lazy, node->leftSon, left, middle));
assert(apply(node->lazy, node->rightSon, middle + 1, right));
node->lazy = Operation();
}
}
template<typename Data, typename Operation, template<typename> typename Allocator>
void
SegmentTree<Data, Operation, Allocator>::updateImpl(SegmentTree::Node *node, size_t left, size_t right, size_t from,
size_t to, const Operation &op) {
if (to < left || right < from) { return; }
if (from <= left && right <= to && apply(op, node, left, right)) { return; }
assert(left != right);
push(node, left, right);
auto middle = getMiddle(left, right);
updateImpl(node->leftSon, left, middle, from, to, op);
updateImpl(node->rightSon, middle + 1, right, from, to, op);
node->data = Data(node->leftSon->data, node->rightSon->data);
}
template<typename Data, typename Operation, template<typename> typename Allocator>
bool SegmentTree<Data, Operation, Allocator>::apply(const Operation &op, SegmentTree::Node *node, size_t left,
size_t right) {
node->lazy = Operation(node->lazy, op);
return op.apply(node->data, left, right);
}
template<typename Data, typename Operation, template<typename> typename Allocator>
Data SegmentTree<Data, Operation, Allocator>::queryImpl(SegmentTree::Node *node, size_t left, size_t right, size_t from,
size_t to) {
if (to < left || right < from) { return Data(); }
if (from <= left && right <= to) { return node->data; }
assert(left != right);
push(node, left, right);
auto middle = getMiddle(left, right);
return Data(queryImpl(node->leftSon, left, middle, from, to),
queryImpl(node->rightSon, middle + 1, right, from, to));
}
int main() {
int n, q;
cin >> n >> q;
std::vector<int> v(n);
cin >> v;
SegmentTree<MaxData<int>, SetOperation<int>> tree(v);
for (int i = 0, type, a, b; i < q; i++) {
cin >> type >> a >> b;
if (type == 0) {
std::cout << tree.query(a - 1, b - 1).max << "\n";
} else {
tree.update(a - 1, a - 1, b);
}
}
return 0;
}