Problem:
Problem I : RMQ

n 個の数字a0 ,a1 , ... ,an-1 とq が与えられる。

q 個のクエリーに対して適切な処理を行なって欲しい。

クエリーは以下の3種類の操作が存在する。

値のシフトを行う

l とr のペアが与えられる。(l < r )
al から ar までの値を Circular Shiftさせる。

0 1 2 3 4 5 6 7 8 9

に対してl=2,r=5というクエリーが与えられたとする。

シフトを行った数字列は

0 1 5 2 3 4 6 7 8 9

となる。

最小値を求める

l とr のペアが与えられる。(l ≤ r )

al から arまでの値の中で最小の値を求める。

値の更新

pos と val のペアが与えられる。
apos の値を val に更新する。

Input

入力は以下のフォーマットで与えられる。

n q
a0
a1
.
.
.
an-1
x1 y1 z1
x2 y2 z2
.
.
.
xq yq zq

xi = 0 なら、シフトのクエリーを表す。このとき、l = yi , r = zi とする。

xi = 1 なら、最小値を求めるクエリーを表す。このとき、l = yi , r = zi とする。

xi = 2 なら、値を更新するクエリーを表す。このとき、pos = yi , val = zi とする。

入力は以下の制約を満たす

1 ≤ n , q ≤ 200,000

0 ≤ ai ≤ 10,000,000

値を更新するクエリーについて、0 ≤ val ≤ 10,000,000

Output

クエリーとしてxi = 1 が与えられたときに、答えの値を１行に出力せよ。

Sample Input 1

10 3
0
1
2
3
4
5
6
7
8
9
1 5 7
0 2 5
1 5 7

Sample Output 1

5
4

Sample Input 2

10 10
289383
930886
692777
636915
747793
238335
885386
760492
516649
641421
2 7 368690
1 3 6
0 2 6
1 1 8
0 2 9
2 2 665123
1 5 9
0 2 8
2 7 961393
1 1 2

Sample Output 2

238335
238335
238335
368690

Solution in C++11 (judge verdict: Accepted)

#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 AATree {
    struct Node;
    typedef Node* NP;
    constexpr static D INF = 1LL<<55;
    static Node last_d;
    static NP last;
    struct Node {
        NP l, r;
        int level, sz;
        D v, mi;
        Node(): l(nullptr), r(nullptr), level(0), sz(0) {}
        Node(D vv): l(last), r(last), level(1), sz(1) {
            v = vv;
            mi = vv;
        }

        //メモリプールをしたい時のためにnewにはラッパを用意しておく
        static NP make() {
            return new Node();
        }
        static NP make(D vv) {
            return new Node(vv);
        }

        void update() {
            sz = 1+l->sz+r->sz;
            mi = v;
            if (l->sz) {
                mi = min(mi, l->mi);
            }
            if (r->sz) {
                mi = min(mi, r->mi);
            }
        }
        NP at_ptr(int k) {
            if (k == l->sz) return this;
            if (k < l->sz) {
                return l->at_ptr(k);
            } else {
                return r->at_ptr(k - (l->sz+1));
            }
        }
        D minv(int a, int b) {
            if (!sz || b <= 0 || sz <= a) return INF;
            if (a <= 0 && sz <= b) return mi;
            D res = INF;
            res = min(res, l->minv(a, b));
            if (a <= l->sz && l->sz < b) {
                res = min(res, v);
            }
            res = min(res, r->minv(a - (l->sz+1), b - (l->sz+1)));
            return res;
        }
        void set(int k, D x) {
            if (k == l->sz) {
                v = x;
                update();
                return;
            }
            if (k < l->sz) {
                l->set(k, x);
            } else {
                r->set(k - (l->sz+1), x);
            }
            update();
        }

        //デバッグ用 tpは木を全部出力してallcheckは木がAA木の条件を満たしているかチェックする
        //木を全て出力する
        void tp(int dps = 0) {
            if (!sz) return;
            string s = "[{(", ss = "]})";
            dps = min(dps, 2);
            cout << s[dps];
            l->tp(dps+1);
            cout << v << " ";
            r->tp(dps+1);
            cout << ss[dps];
        }
        //木がAA木の条件を満たしているかチェックする
        void allcheck() {
            if (sz) {
                assert(level);
            } else {
                assert(this == last);
                assert(!level);
            }
            if (!sz) return;
            assert(l);
            assert(r);
            assert(sz == l->sz+1+r->sz);
            assert(l->level == level-1);
            if (r->level == level) {
                assert(r->r->level == level-1);
            }
            l->allcheck();
            r->allcheck();
        }
    } *n;
    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;
        n->update();
        return n;
    }
    AATree() : n(last) {}
    AATree(NP n) : n(n) {}
    AATree(int sz, D d[]) {
        n = built(sz, d);
    }

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

    NP insert(NP n, int k, D x) {
        if (!n->sz) {
            assert(k == 0);
            return Node::make(x);
        }
        if (k <= n->l->sz) {
            n->l = insert(n->l, k, x);
            n->update();
            n = skew(n);
        } else {
            n->r = insert(n->r, k - (n->l->sz+1), x);
            n->update();
        }
        n = pull(n);
        return n;
    }
    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;
    }
    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;
    }
    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--;
            n = superleftdown(n);
        } else {
            n = leftdown(n);
            n->l = superleftdown(n->l);
            n = superleftdown(n);
        }
        return n;
    }
    NP superrightdown(NP n) {
        if (n->r->level >= n->level-1) return n;
        if (n->l->level != n->l->r->level) {
            n->level--;
            n = skew(n);
            n->r = superrightdown(n->r);
        } else {
            n = rightdown(n);
            n->r = superrightdown(n->r);
            n = rightdown(n);
        }
        return n;
    }
    NP remove(NP n, int k) {
        if (k == n->l->sz) {
            if (n->level == 1) {
                return n->r;
            }
            NP nn = n->r->at_ptr(0);
            nn->r = remove(n->r, 0);//この行と
            nn->l = n->l;//この行を入れ替えたら正しく動かないので注意
            nn->level = n->level;
            nn->update();
            return rightdown(nn);
        }
        if (k < n->l->sz) {
            n->l = remove(n->l, k);
            n->update();
            return leftdown(n);
        } else {
            n->r = remove(n->r, k - (n->l->sz+1));
            n->update();
            return rightdown(n);
        }
    }
    NP merge(NP l, NP r) {
        if (!l->sz) return r;
        if (!r->sz) return l;
        NP n;
        if (l->level == r->level) {
            NP n = r->at_ptr(0);
            n->r = remove(r, 0);
            n->l = l;
            n->level = l->level+1;
            n->update();
            return rightdown(n);
        }
        if (l->level > r->level) {
            l->r = merge(l->r, r);
            n = l;
        } else {
            r->l = merge(l, r->l);
            n = r;
        }
        n->update();
        n = skew(n);
        n = pull(n);
        return n;
    }

    pair<NP, NP> split(NP n, int k) {
        if (!n->sz) return {last, last};
        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 tp() {
        n->tp();
        printf("\n");
    }
    void allcheck() {
        n->allcheck();
    }
    //木の基本動作4種
    void merge(AATree r) {
        n = merge(n, r.n);
    }
    AATree split(int k) {
        auto y = split(n, k);
        n = y.first;
        return AATree(y.second);
    }
    void insert(int k, D x) {
        n = insert(n, k, x);
    }
    void remove(int k) {
        n = remove(n, k);
    }

    NP get_ptr(int k) {
        if (k < 0 || sz() <= k) return last;
        return n->get_ptr(k);
    }
    D minv(int l, int r) {
        return n->minv(l, r);
    }
    void set(int k, D x) {
        n->set(k, x);
        return;
    }
};
template<class D>
typename AATree<D>::Node AATree<D>::last_d = AATree<D>::Node();
template<class D>
typename AATree<D>::NP AATree<D>::last = &AATree<D>::last_d;

const int MN = 200200;
ll a[MN];
int main() {
    int n, q;
    cin >> n >> q;
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }
    AATree<ll> atr(n, a);
    for (int i = 0; i < q; i++) {
        int x, y, z;
        cin >> x >> y >> z;
        if (x == 0) {
            int l = y, r = z;
            auto at0 = atr.split(r+1);
            auto at1 = atr.split(r);
            auto at2 = atr.split(l);
            atr.merge(at1);
            atr.merge(at2);
            atr.merge(at0);
        } else if (x == 1) {
            int l = y, r = z; r++;
            printf("%lld\n", atr.minv(l, r));
        } else {
            int l = y, val = z;
            atr.set(l, val);
        }
        //atr.tp();
    }
    return 0;
}