#define NDEBUG 1
#include <set>
#include <tuple>
#include <vector>
#include <iostream>
#include <cassert>
#include <algorithm>
#include <sstream>
using namespace std;
using ll = long long;
#define FOR(i, a, b) for (int i = (a); i < int(b); ++i)
#define RFOR(i, a, b) for (int i = (b)-1; i >= int(a); --i)
#define rep(i, n) FOR(i, 0, n)
#define rep1(i, n) FOR(i, 1, int(n) + 1)
#define rrep(i, n) RFOR(i, 0, n)
#define rrep1(i, n) RFOR(i, 1, int(n) + 1)
#define all(c) begin(c), end(c)
const int MOD = 1000000007;

const int INF = 1e9;

template <typename T>
void __dump__(std::ostream &os, const T &first) {
    os << first;
}
template <typename First, typename... Rest>
void __dump__(std::ostream &os, const First &first, const Rest &... rest) {
    os << first << ", ";
    __dump__(os, rest...);
}
#define dump(...)                                         \
    do {                                                  \
        std::ostringstream os;                            \
        os << __LINE__ << ":\t" << #__VA_ARGS__ << " = "; \
        __dump__(os, __VA_ARGS__);                        \
        std::cerr << os.str() << std::endl;               \
    } while (0)

struct node {
    int key;
    node* left;
    node* right;
    int height;
    int size;
    int min;
    node(int k) : key(k), left(nullptr), right(nullptr), height(1), size(1), min(k) { }
};

using np = node*;

int height(node* p) {
    return p ? p->height : 0;
}

int size(np p) {
    return p ? p->size : 0;
}

int minimum(np p) {
    return p ? p->min : INF;
}

int bfactor(np p) {
    return height(p->right) - height(p->left);
}

np update(np p) {
    p->height = max(height(p->left), height(p->right)) + 1;
    p->size = size(p->left) + size(p->right) + 1;
    p->min = min({minimum(p->left), minimum(p->right), p->key});
    return p;
}

np rotate_right(np p) {
    np q = p->left;
    p->left = q->right;
    q->right = p;
    update(p);
    update(q);
    return q;
}

np rotate_left(np q) {
    np p = q->right;
    q->right = p->left;
    p->left = q;
    update(q);
    update(p);
    return p;
}

np balance(np p) {
    int bf = bfactor(p);
    if (bf == 2) {
        if (bfactor(p->right) < 0) p->right = rotate_right(p->right);
        return rotate_left(p);
    }
    if (bf == -2) {
        if (bfactor(p->left) > 0) p->left = rotate_left(p->left);
        return rotate_right(p);
    }
    assert(abs(bfactor(p)) < 2);
    return p;
}

np insert(np p, int x) {
    if (!p) return new node(x);
    if (x < p->key) p->left = insert(p->left, x);
    else p->right = insert(p->right, x);
    return balance(update(p));
}

np insert_at(np p, int k, int x) {
    if (!p) return new node(x);
    int sl = size(p->left);
    if (k <= sl) p->left = insert_at(p->left, k, x);
    else p->right = insert_at(p->right, k - sl - 1, x);
    return balance(update(p));
}

np find_min(np p) {
    return p->left ? find_min(p->left) : p;
}

np find_max(np p) {
    return p->right ? find_max(p->right) : p;
}

np remove_min(np p) {
    if (p->left == 0) return p->right;
    p->left = remove_min(p->left);
    update(p);
    return balance(p);
}

np remove_max(np p) {
    if (p->right == 0) return p->left;
    p->right = remove_max(p->right);
    update(p);
    return balance(p);
}

np remove(np n, int k) {
    if (!n) return nullptr;
    if (k < n->key) n->left = remove(n->left, k);
    else if (k > n->key) n->right = remove(n->right, k);
    else {
        np l = n->left;
        np r = n->right;
        delete n;
        if (!r) return l;
        np min = find_min(r);
        min->right = remove_min(r);
        min->left = l;
        update(n);
        return balance(update(min));
    }
    update(n);
    return balance(n);
}

np remove_at(np n, int k) {
    if (!n) return nullptr;
    int sl = size(n->left);
    if (k < sl) n->left = remove_at(n->left, k);
    else if (sl < k) n->right = remove_at(n->right, k - sl - 1);
    else {
        np l = n->left;
        np r = n->right;
        delete n;
        if (!r) return l;
        np min = find_min(r);
        min->right = remove_min(r);
        min->left = l;
        return balance(update(min));
    }
    update(n);
    return balance(n);
}

int at(np n, int k) {
    if (n == nullptr) return -1;
    int ls = size(n->left);
    if (k < ls) {
        return at(n->left, k);
    } else if (ls == k) {
        return n->key;
    } else {
        return at(n->right, k - ls - 1);
    }
}

void upd(np n, int k, int x) {
    int sl = size(n->left);
    if (k < sl) {
        upd(n->left, k, x);
    } else if (k == sl) {
        n->key = x;
    } else {
        upd(n->right, k - sl - 1, x);
    }
    update(n);
}

int rmq(np n, int l, int r) {
    if (l == 0 && r == n->size) {
        return n->min;
    } else {
        int sl = size(n->left);
        int res = INF;
        if (l < sl) {
            res = min(res, rmq(n->left, l, min(r, sl)));
        }
        if (l <= sl && sl < r) {
            res = min(res, n->key);
        }
        if (sl + 1 < r) {
            res = min(res, rmq(n->right, max(0, l - sl - 1), r - sl - 1));
        }
        return res;
    }
}

void show(node *p, int d = 0) {
    if (p) {
        show(p->left, d + 1);
        cout << string(d*2, ' ');
        cout << "(" << p->key << ", " << p->min << ")" << endl;
        show(p->right, d + 1);
    }
}

np merge_into_left(np t1, np par, np t2) {
    if (height(t1) > height(t2) + 1) {
        t1->right = merge_into_left(t1->right, par, t2);
        update(t1);
        return balance(t1);
    } else {
        par->left = t1;
        par->right = t2;
        update(par);
        assert(abs(bfactor(par)) < 2);
        return par;
    }
}

np merge_into_right(np t1, np par, np t2) {
    if (height(t1) + 1 < height(t2)) {
        t2->left = merge_into_right(t1, par, t2->left);
        update(t2);
        return balance(t2);
    } else {
        par->left = t1;
        par->right = t2;
        update(par);
        assert(abs(bfactor(par)) < 2);
        return par;
    }
}

np merge(np t1, np t2) {
    if (t1 == nullptr) return t2;
    if (t2 == nullptr) return t1;
    if (size(t1) > size(t2)) {
        np x = find_min(t2);
        t2 = remove_min(t2);
        return merge_into_left(t1, x, t2);
    } else {
        np x = find_max(t1);
        t1 = remove_max(t1);
        return merge_into_right(t1, x, t2);
    }
}

np merge(np t1, np par, np t2) {
    assert(size(par) == 1);
    if (size(t1) > size(t2)) {
        return merge_into_left(t1, par, t2);
    } else {
        return merge_into_right(t1, par, t2);
    }
}

pair<np, np> split_at(np t, int k) {
    assert(0 <= k && k <= size(t));
    if (t == nullptr) return { nullptr, nullptr };
    np l = t->left, r = t->right;
    t->left = t->right = nullptr;
    update(t);
    int sl = size(l);
    if (k < sl) {
        np ll, lr;
        tie(ll, lr) = split_at(l, k);
        r = merge(lr, t, r);
        return {ll, r};
    } else if (k == sl) {
        r = merge(t, r);
        return {l, r};
    } else {
        np rl, rr;
        tie(rl, rr) = split_at(r, k - sl - 1);
        l = merge(l, t, rl);
        return {l, rr};
    }
}

pair<np, np> split(np t, int x) {
    if (t == nullptr) return { nullptr, nullptr };
    np l = t->left, r = t->right;
    t->left = t->right = nullptr;
    update(t);
    if (x < t->key) {
        np ll, lr;
        tie(ll, lr) = split(l, x);
        r = merge(lr, t, r);
        return {ll, r};
    } else if (x == t->key) {
        r = merge(t, r);
        return {l, r};
    } else {
        np rl, rr;
        tie(rl, rr) = split(r, x);
        l = merge(l, t, rl);
        return {l, rr};
    }
}

namespace test {
    void test(np n) {
        if (n) {
            assert(bfactor(n) < 2);
            if (n->left) assert(n->left->key < n->key);
            if (n->right) assert(n->key < n->right->key);
            assert(n->size = size(n->left) + size(n->right) + 1);
            assert(n->height = 1 + max(size(n->left), size(n->right)));
            test(n->left);
            test(n->right);
        }
    }

    void to_array(np n, vector<int> &a) {
        if (n) {
            to_array(n->left, a);
            a.push_back(n->key);
            to_array(n->right, a);
        }
    }

    // void test_merge() {
    //     rep(i, 100000) {
    //         int n = rand() % 10;
    //         int m = rand() % 10;
    //         vector<int> a;
    //         {
    //             set<int> s;
    //             while ((int)s.size() < n + m) {
    //                 s.insert(rand() % 1000);
    //             }
    //             for (auto &x : s) {
    //                 a.push_back(x);
    //             }
    //         }
    //         np t1 = nullptr;
    //         rep(i, n) t1 = insert(t1, a[i]);
    //         np t2 = nullptr;
    //         rep(i, m) t2 = insert(t2, a[n + i]);
    //         np t = merge(t1, t2);
    //         vector<int> na;
    //         to_array(t, na);
    //         assert(a == na);
    //     }
    // }

    // void test_split_at() {
    //     rep(i, 100000) {
    //         if (i % 100) dump(i);
    //         int n = rand() % 100;
    //         vector<int> a;
    //         {
    //             set<int> s;
    //             while ((int)s.size() < n) {
    //                 s.insert(rand() % 1000);
    //             }
    //             for (auto &x : s) {
    //                 a.push_back(x);
    //             }
    //         }
    //         np t = nullptr;
    //         rep(i, n) t = insert(t, a[i]);
    //         int k = rand() % (n + 1);
    //         np l, r;
    //         tie(l, r) = split_at(t, k);
    //         vector<int> na;
    //         to_array(l, na);
    //         assert((int)na.size() == k);
    //         to_array(r, na);
    //         assert((int)na.size() == n);
    //         assert(a == na);
    //     }
    // }
}

np insert_ms(np n, int x) {
    np l, r;
    tie(l, r) = split(n, x);
    return merge(l, new node(x), r);
}

np remove_ms(np n, int k) {
    np l, r;
    tie(l, r) = split_at(n, k);
    np rr = remove_min(r);
    return merge(l, rr);
}

np build(int *l, int *r) {
    int n = r - l;
    if (n <= 0) return nullptr;
    int *m = l + n / 2;
    np res = new node(*m);
    res->left = build(l, m);
    res->right = build(m + 1, r);
    return update(res);
}

int main(){
    // cin.tie(0);
    // ios::sync_with_stdio(0);
    int n, q;
    while (cin >> n >> q) {
        static int a[200010];
        rep(i,n) cin >> a[i];
        np root = build(a, a + n);
        // show(root);
        rep(i, q){
            int op;
            cin >> op;
            if(false){
            } else if(op == 0){
                int l, r;
                cin >> l >> r;
                int tmp = at(root, r);
                root = remove_at(root, r);
                // dump(l, r, tmp);
                // cout << "=== removed" << endl;
                // show(root);
                // cout << "===" << endl;
                root = insert_at(root, l, tmp);
            } else if(op == 1){
                int l, r;
                cin >> l >> r;
                cout << rmq(root, l, r + 1) << endl;
            } else if(op == 2){
                int p, v;
                cin >> p >> v;
                upd(root, p, v);
            }
            // cout << "=== show" << endl;
            // show(root);
            // cout << "===" << endl;
        }
    }
}

Measured memory_bytes: 13252