#define NDEBUG
 
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#include <queue>
#include <set>
#include <tuple>
#include <cassert>
   
using namespace std;
typedef long long ll;
 
template<class D>
struct AASSTree {
    struct Node;
    typedef Node* NP;
    const static D INF = 1<<30;
    static Node last_d;
    static NP last;
    struct Node {
        NP l, r;
        int level, sz;
        D v, mi, lz;
        Node(): l(nullptr), r(nullptr), level(0), sz(0) {}
        Node(D vv): l(last), r(last), level(1), sz(1) {
            v = vv;
            mi = vv;
            lz = 0;
        }

        //メモリプールをしたい時のためにnewにはラッパを用意しておく
        static NP make() {
            return new Node();
        }
        static NP make(D vv) {
            return new Node(vv);
        }
    } *n;
    static NP push(NP n) {
        if (n->lz) {
            if (n->l != last) {
                lzdata(n->l, n->lz);
            }
            if (n->r != last) {
                lzdata(n->r, n->lz);
            }
            n->lz = 0;
        }
        return n;
    }
    //k番目の要素のポインタを取得する
    static NP at_ptr(NP n, int k) {
        if (k == n->l->sz) return n;
        push();
        if (k < n->l->sz) {
            return at_ptr(n->l, k);
        } else {
            return at_ptr(n->r, k - (n->l->sz+1));
        }
    }

    static void update(NP n) {
        n->sz = 1+n->l->sz+n->r->sz;
        assert(!n->lz);
        n->mi = n->v;
        if (n->l != last) {
            n->mi = min(n->mi, n->l->mi);
        }
        if (n->r != last) {
            n->mi = min(n->mi, n->r->mi);
        }
    }

    static void lzdata(NP n, D x) {
        n->v += x;
        n->mi += x;
        n->lz += x;
    }
    static D at(NP n, int k) {
        if (k == n->l->sz) return n->v;
        push(n);
        if (k < n->l->sz) {
            return at(n->l, k);
        } else {
            return at(n->r, k - (n->l->sz+1));
        }
    }
    static void tp(NP n) {
        if (!n->sz) return;
        push(n);
        tp(n->l);
        cout << n->v << " ";
        tp(n->r);
    }

    static void add(NP n, int a, int b, D x) {
        if (!n->sz || b <= 0 || n->sz <= a) return;
        if (a <= 0 && n->sz <= b) {
            lzdata(n, x);
            return;
        }
        push(n);
        add(n->l, a, b, x);
        if (a <= n->l->sz && n->l->sz < b) {
            n->v += x;
        }
        add(n->r, a - (n->l->sz+1), b - (n->l->sz+1), x);
        update();
    }
    static D get(NP n, int a, int b) {
        if (!n->sz || b <= 0 || n->sz <= a) return INF;
        if (a <= 0 && n->sz <= b) {
            return n->mi;
        }
        push(n);
        D res = INF;
        res = min(res, get(n->l, a, b));
        if (a <= n->l->sz && n->l->sz < b) {
            res = min(res, n->v);
        }
        res = min(res, get(n->r, a - (n->l->sz+1), b - (n->l->sz+1)));
        return res;
    }
    static void set(NP n, int k, D x) {
        if (k == n->l->sz) {
            n->v = x;
            update(n);
            return;
        }
        if (k < n->l->sz) {
            set(n->l, k, x);
        } else {
            set(n->r, k - (n->l->sz+1), x);
        }
        update(n);
        return;
    }
    static void allpush(NP n) {
        if (!n->sz) return;
        push(n);
        allpush(n->l);
        allpush(n->r);
    }
    static NP built(int sz, D d[]) {
        if (!sz) return last;
        int md = (sz-1)/2;
        NP n = Node::make(d[md]);
        n->l = built(md, d);
        n->r = built(sz-(md+1), d+(md+1));
        n->level = n->l->level+1;
        update(n);
        return n;
    }
    AASSTree() : n(last) {}
    AASSTree(NP n) : n(n) {}
    //木の初期化はn回insertより一気に作る方が有意に速くなる事が多い
    AASSTree(int sz, D d[]) {
        n = built(sz, d);
    }

    //AA木の基本動作であるskew/split splitは名前が紛らわしいためpullに変更してある
    static NP skew(NP n) {
        if (n->level == n->l->level) {
            NP L = n->l;
            push(n); push(L);
            n->l = L->r;
            L->r = n;
            update(n); update(L);
            return L;
        }
        return n;
    }
    static NP pull(NP n) {
        if (n->level == n->r->level && n->r->level == n->r->r->level) {
            NP R = n->r;
            push(n); push(R);
            n->r = R->l;
            R->l = n;
            R->level++;
            update(n); update(R);
            return R;
        }
        return n;
    }

    static NP insert(NP n, int k, D x) {
        if (n == last) {
            assert(k == 0);
            return Node::make(x);
        }
        push(n);
        if (k <= n->l->sz) {
            n->l = insert(n->l, k, x);
            update(n);
        } else {
            n->r = insert(n->r, k - (n->l->sz+1), x);
            update(n);
        }
        n = skew(n);
        n = pull(n);
        return n;
    }
    static NP leftdown(NP n) {
        assert(n->l->level < n->level);
        if (n->l->level == n->level-1) return n;
        n->level--;
        if (n->r->level == n->level) {
            n = pull(n);
        } else {
            n->r->level--;
            n->r = skew(n->r);
            n->r->r = skew(n->r->r);
            n = pull(n);
            n->r = pull(n->r);
        }
        return n;
    }
    static NP rightdown(NP n) {
        assert(n->r->level <= n->level);
        if (n->r->level >= n->level-1) return n;
        n->level--;
        n = skew(n);
        n->r = skew(n->r);
        n = pull(n);
        return n;
    }
    static NP superleftdown(NP n) {
        if (n->l->level == n->level-1) return n;
        if (n->level != n->r->level && n->r->level != n->r->r->level) {
            n->level--;
            return superleftdown(n);
        }
        n = leftdown(n);
        n->l = superleftdown(n->l);
        n = leftdown(n);
        return n;
    }
    static NP superrightdown(NP n) {
        if (n->r->level >= n->level-1) return n;
        n = rightdown(n);
        n->r = superrightdown(n->r);
        n = rightdown(n);
        return n;
    }

    //pair<0番目の要素,0番目の要素を削除した木>
    static pair<NP, NP> at0_with_remove(NP n) {
        push(n);
        if (n->l == last) {
            return {n, n->r};
        }
        auto x = at0_with_remove(n->l);
        n->l = x.second;
        update(n);
        x.second = leftdown(n);
        return x;
    }
    static NP remove(NP n, int k) {
        assert(n != last);
        push(n);
        if (k == n->l->sz) {
            if (n->level == 1) {
                return n->r;
            }
            auto x = at0_with_remove(n->r);
            NP nn = x.first;
            push(nn);
            nn->l = n->l;
            nn->r = x.second;
            nn->level = n->level;
            update(nn);
            return rightdown(nn);
        }
        if (k < n->l->sz) {
            n->l = remove(n->l, k);
            update(n);
            return leftdown(n);
        } else {
            n->r = remove(n->r, k - (n->l->sz+1));
            update(n);
            return rightdown(n);
        }
    }
    static NP merge(NP l, NP r) {
        if (l == last) return r;
        if (r == last) return l;
        if (l->level == r->level) {
            auto x = at0_with_remove(r);
            NP n = x.first;
            push(n);
            n->r = x.second;
            n->l = l;
            n->level = l->level+1;
            update(n);
            return rightdown(n);
        }
        NP n;
        if (l->level > r->level) {
            push(l);
            l->r = merge(l->r, r);
            update(l);
            n = l;
        } else {
            push(r);
            r->l = merge(l, r->l);
            update(r);
            n = r;
        }
        n = skew(n);
        n = pull(n);
        return n;
    }
    static pair<NP, NP> split(NP n, int k) {
        if (n == last) return {last, last};
        n->push();
        if (k <= n->l->sz) {
            auto y = split(n->l, k);
            n->l = y.second;
            n->update();
            n = superleftdown(n);
            y.second = n;
            return y;
        } else {
            auto y = split(n->r, k- (n->l->sz+1));
            n->r = y.first;
            n->update();
            n = superrightdown(n);
            y.first = n;
            return y;
        }
    }

    int sz() {
        return n->sz;
    }
    //基本動作
    void merge(AASSTree r) {
        n = merge(n, r.n);
    }
    AASSTree split(int k) {
        auto y = split(n, k);
        n = y.first;
        return AASSTree(y.second);
    }
    void insert(int k, D x) {
        n = insert(n, k, x);
    }
    void remove(int k) {
        n = remove(n, k);
    }
    D at(int k) {
        return at(n, k);
    }
    void tp() {
        n->tp();
        printf("\n");
    }
    void add(int l, int r, D x) {
        n->add(n, l, r, x);
    }
    void set(int k, D x) {
        set(n, k, x);
    }
    D get(int l, int r) {
        return get(n, l, r);
    }
    void allpush() {
        allpush(n);
        return;
    }
};
template<class D>
typename AASSTree<D>::Node AASSTree<D>::last_d = AASSTree<D>::Node();
template<class D>
typename AASSTree<D>::NP AASSTree<D>::last = &AASSTree<D>::last_d;

const int MN = 200200;
int a[MN];
int main() {
    int n, q;
    scanf("%d %d", &n, &q);
    for (int i = 0; i < n; i++) {
        scanf("%d", a+i);
    }
    AASSTree<int> atr(n, a);
    for (int i = 0; i < q; i++) {
        int x, y, z;
        scanf("%d %d %d", &x, &y, &z);
        if (x == 0) {
            int l = y, r = z;
            int tmp = atr.at(r);
            atr.remove(r);
            atr.insert(l, tmp);
        } else if (x == 1) {
            int l = y, r = z; r++;
            printf("%d\n", atr.get(l, r));
        } else {
            int l = y, val = z;
            atr.set(l, val);
        }
        //atr.tp();
    }
    return 0;
}

Measured memory_bytes: 20724