using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Diagnostics;
//using System.Numerics;
using Enu = System.Linq.Enumerable;

public class Program
{
    public void Solve()
    {
        Console.SetOut(new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = false });
        int N = Reader.Int(), Q = Reader.Int();
        var A = Reader.IntArray(N);
        var tree = new AVLTree();
        for (int i = 0; i < N; i++) tree.Insert(i, A[i]);

        foreach (var q in Reader.IntTable(Q))
            if (q[0] == 0)
            {
                int L = q[1], R = q[2] + 1, len = R - L;
                var sp = tree.Split(L, R);
                var mid = sp[1].Split(len - 1);
                var b = mid[1].Merge(mid[0]);
                tree = sp[0].Merge(b).Merge(sp[2]);
            }
            else if (q[0] == 1)
                Console.WriteLine(tree.Min(q[1], q[2] + 1));
            else
                tree[q[1]] = q[2];

        Console.Out.Flush();
        Console.ReadLine();
    }
}


public class AVLTree
{
    public struct Node
    {
        public int L, R, Count;
        public byte MaxDepth;
        public long Val;
        //public long Sum;
        public long Min;
    }
    const int PoolSize = (int)1e6;
    const int None = 0, NotFound = -1;
    static Node[] nodes = new Node[PoolSize + 1];
    static int nodeIndex = 1;
    int Root;

    public int Count { get { return nodes[Root].Count; } }
    public int MaxDepth { get { return nodes[Root].MaxDepth; } }

    public AVLTree() { Root = None; }
    private AVLTree(int rootIndex) { Root = rootIndex; }
    static AVLTree() { nodes[None].Min = long.MaxValue; }

    public void Clear() { Root = None; }

    int GenNode(long val)
    {
        nodes[nodeIndex].MaxDepth = 1;
        nodes[nodeIndex].Count = 1;
        nodes[nodeIndex].L = None;
        nodes[nodeIndex].R = None;
        nodes[nodeIndex].Val = val;
        //nodes[nodeIndex].Sum = val;
        nodes[nodeIndex].Min = val;
        return nodeIndex++;
    }

    void Update(int i)
    {
        Debug.Assert(i > None);
        int L = nodes[i].L, R = nodes[i].R;
        nodes[i].Count = 1 + nodes[L].Count + nodes[R].Count;
        //nodes[i].Sum = nodes[i].Val + nodes[L].Sum + nodes[R].Sum;
        nodes[i].Min = Math.Min(nodes[i].Val, Math.Min(nodes[L].Min, nodes[R].Min));
        nodes[i].MaxDepth = (byte)(1 + Math.Max(nodes[L].MaxDepth, nodes[R].MaxDepth));
    }

    public long this[int index]
    {
        get
        {
            if (index < 0 || index >= Count) throw new ArgumentException();
            return nodes[GetNodeIndex(index)].Val;
        }
        set
        {
            if (index < 0 || index >= Count) throw new ArgumentException();
            SetRec(Root, index, value);
        }
    }

    void SetRec(int i, int index, long val)
    {
        int Lsize = nodes[nodes[i].L].Count;
        if (index == Lsize) nodes[i].Val = val;
        else if (index < Lsize) SetRec(nodes[i].L, index, val);
        else SetRec(nodes[i].R, index - Lsize - 1, val);
        Update(i);
    }

    public void Insert(int index, long val)
    {
        if (index < 0 || index > Count) throw new ArgumentException();
        Root = Insert(Root, index, GenNode(val));
    }
    int Insert(int i, int index, int newNodeIndex)
    {
        if (i == None) return newNodeIndex;
        int Lsize = nodes[nodes[i].L].Count;
        if (index <= Lsize)
            nodes[i].L = Insert(nodes[i].L, index, newNodeIndex);
        else
            nodes[i].R = Insert(nodes[i].R, index - Lsize - 1, newNodeIndex);
        Update(i);
        return Rebalance(i);
    }

    public void RemoveAt(int index)
    {
        if (index < 0 || index >= Count) throw new ArgumentException();
        Root = RemoveAt(index, Root);
    }
    int RemoveAt(int index, int i)
    {
        int Lsize = nodes[nodes[i].L].Count;
        if (index == Lsize) return RemoveNode(i);
        if (index < Lsize)
            nodes[i].L = RemoveAt(index, nodes[i].L);
        else
            nodes[i].R = RemoveAt(index - Lsize - 1, nodes[i].R);
        Update(i);
        return Rebalance(i);
    }
    int RemoveNode(int i)
    {
        int L = nodes[i].L, R = nodes[i].R;
        if (L == None && R == None) return None;
        if (L == None) return R;
        if (R == None) return L;
        int minNode = 0;
        R = MoveMin(R, ref minNode);
        nodes[minNode].L = L;
        nodes[minNode].R = R;
        Update(minNode);
        return Rebalance(minNode);
    }
    int MoveMin(int i, ref int minNode)
    {
        if (nodes[i].L == None) { minNode = i; return nodes[i].R; }
        nodes[i].L = MoveMin(nodes[i].L, ref minNode);
        Update(i);
        return Rebalance(i);
    }


    public void AddVal(int index, long val) { AddVal(Root, index, val); }
    void AddVal(int i, int index, long val)
    {
        int Lsize = nodes[nodes[i].L].Count;
        if (index == Lsize) nodes[i].Val += val;
        else if (index < Lsize) AddVal(nodes[i].L, index, val);
        else AddVal(nodes[i].R, index - Lsize - 1, val);
        Update(i);
    }


    public AVLTree[] Split(int index)
    {
        if (index < 0 || index > Count) throw new ArgumentException();
        int R;
        Split(Root, index, out Root, out R);
        return new[] { new AVLTree(Root), new AVLTree(R) };
    }
    public AVLTree[] Split(int L, int R)
    {
        if (L < 0 || L > R || R > Count) throw new ArgumentException();
        int a, b, c;
        Split(Root, L, R, out a, out b, out c);
        return new[] { new AVLTree(a), new AVLTree(b), new AVLTree(c) };
    }

    void Split(int i, int index, out int L, out int R)
    {
        if (i == None) { L = R = None; return; }
        int Lch = nodes[i].L, Rch = nodes[i].R;
        nodes[i].L = nodes[i].R = None;
        Update(i);
        int Lsize = nodes[Lch].Count;

        if (Lsize == index)
        {
            L = Lch;
            R = AddFirst(Rch, i);
        }
        else if (index < Lsize)
        {
            int t;
            Split(Lch, index, out L, out t);
            R = Join(t, i, Rch);
        }
        else
        {
            int t;
            Split(Rch, index - Lsize - 1, out t, out R);
            L = Join(Lch, i, t);
        }
    }
    void Split(int node, int L, int R, out int a, out int b, out int c)
    {
        Split(node, R, out b, out c);
        Split(b, L, out a, out b);
    }


    public AVLTree Merge(AVLTree right)
    {
        Root = Merge(Root, right.Root);
        return this;
    }
    int Merge(int L, int R)
    {
        if (L == None) return R;
        if (R == None) return L;
        int Rfirst;
        R = SplitFirst(R, out Rfirst);
        return Join(L, Rfirst, R);
    }


    int Join(int L, int node, int R)
    {
        if (nodes[L].Count == 0) return AddFirst(R, node);
        if (nodes[R].Count == 0) return AddLast(L, node);
        int diff = nodes[L].MaxDepth - nodes[R].MaxDepth;
        if (diff > 2)
        {
            nodes[L].R = Join(nodes[L].R, node, R);
            Update(L);
            return Rebalance(L);
        }
        if (diff < -2)
        {
            nodes[R].L = Join(L, node, nodes[R].L);
            Update(R);
            return Rebalance(R);
        }
        nodes[node].L = L;
        nodes[node].R = R;
        Update(node);
        return node;
    }

    int AddFirst(int i, int first)
    {
        if (first == None) return i;
        if (i == None) return first;
        if (nodes[i].L == None) nodes[i].L = first;
        else nodes[i].L = AddFirst(nodes[i].L, first);
        Update(i);
        return Rebalance(i);
    }
    int AddLast(int i, int last)
    {
        if (last == None) return i;
        if (i == None) return last;
        if (nodes[i].R == None) nodes[i].R = last;
        else nodes[i].R = AddLast(nodes[i].R, last);
        Update(i);
        return Rebalance(i);
    }
    int SplitFirst(int i, out int firstNode)
    {
        if (nodes[i].L == None)
        {
            firstNode = i;
            int R = nodes[i].R;
            nodes[i].R = None;
            Update(i);
            return R;
        }
        nodes[i].L = SplitFirst(nodes[i].L, out firstNode);
        Update(i);
        return Rebalance(i);
    }

    int GetNodeIndex(int index)
    {
        int i = Root, Lsize;
        while (index != (Lsize = nodes[nodes[i].L].Count))
            if (index < Lsize) i = nodes[i].L;
            else { index -= Lsize + 1; i = nodes[i].R; }
        return i;
    }


    public long Min(int L, int R) { return Min(Root, L, R); }
    long Min(int i, int L, int R)
    {
        if (i == None || L >= R) return long.MaxValue;
        if (L == 0 && R == nodes[i].Count) return nodes[i].Min;
        long res = long.MaxValue;
        int Lsize = nodes[nodes[i].L].Count;
        if (Lsize >= L && Lsize < R)
            res = nodes[i].Val;
        if (L < Lsize)
            res = Math.Min(res, Min(nodes[i].L, L, Math.Min(R, Lsize)));
        if (R - Lsize - 1 > 0)
            res = Math.Min(res, Min(nodes[i].R, Math.Max(0, L - Lsize - 1), R - Lsize - 1));
        return res;
    }


    public long[] ToArray()
    {
        long[] res = new long[Count];
        if (Count == 0) return res;
        ToArrayRec(Root, 0, res);
        return res;
    }
    void ToArrayRec(int i, int resL, long[] res)
    {
        int Lsize = nodes[nodes[i].L].Count;
        if (Lsize > 0) ToArrayRec(nodes[i].L, resL, res);
        res[resL + Lsize] = nodes[i].Val;
        if (nodes[i].R != None) ToArrayRec(nodes[i].R, resL + Lsize + 1, res);
    }


    int Rebalance(int i)
    {
        int L = nodes[i].L, R = nodes[i].R;
        switch (nodes[L].MaxDepth - nodes[R].MaxDepth)
        {
            case 2:
                if (nodes[nodes[L].L].MaxDepth < nodes[nodes[L].R].MaxDepth)
                    RotateLR(i);
                return RotateLL(i);
            case -2:
                if (nodes[nodes[R].R].MaxDepth < nodes[nodes[R].L].MaxDepth)
                    RotateRL(i);
                return RotateRR(i);
            default:
                return i;
        }
    }
    void RotateLR(int i)
    {
        int A = nodes[i].L, B = nodes[A].R;
        nodes[A].R = nodes[B].L; Update(A);
        nodes[B].L = A; Update(B);
        nodes[i].L = B; Update(i);
    }
    int RotateLL(int i)
    {
        int B = nodes[i].L;
        nodes[i].L = nodes[B].R; Update(i);
        nodes[B].R = i; Update(B);
        return B;
    }
    void RotateRL(int i)
    {
        int A = nodes[i].R, B = nodes[A].L;
        nodes[A].L = nodes[B].R; Update(A);
        nodes[B].R = A; Update(B);
        nodes[i].R = B; Update(i);
    }
    int RotateRR(int i)
    {
        int B = nodes[i].R;
        nodes[i].R = nodes[B].L; Update(i);
        nodes[B].L = i; Update(B);
        return B;
    }

    public bool Exam(int i = -1)
    {
        if (i == -1) i = Root;
        if (i == None) return true;
        int L = nodes[i].L, R = nodes[i].R;
        if (nodes[i].MaxDepth != 1 + Math.Max(nodes[L].MaxDepth, nodes[R].MaxDepth))
            return false;
        if (Math.Abs(nodes[L].MaxDepth - nodes[R].MaxDepth) > 1)
            return false;
        return Exam(L) && Exam(R);
    }
    public void ShowEdges(int i = -1)
    {
        if (i == -1) i = Root;
        if (i == None) return;
        if (nodes[i].L != None)
            Console.WriteLine(nodes[i].Val + " " + nodes[nodes[i].L].Val);
        if (nodes[i].R != None)
            Console.WriteLine(nodes[i].Val + " " + nodes[nodes[i].R].Val);
        ShowEdges(nodes[i].L);
        ShowEdges(nodes[i].R);
    }
}


class Entry { static void Main() { new Program().Solve(); } }
class Reader
{
    static TextReader reader = Console.In;
    static readonly char[] separator = { ' ' };
    static readonly StringSplitOptions op = StringSplitOptions.RemoveEmptyEntries;
    static string[] A = new string[0];
    static int i;
    static void Init() { A = new string[0]; }
    public static void Set(TextReader r) { reader = r; Init(); }
    public static void Set(string file) { reader = new StreamReader(file); Init(); }
    public static bool HasNext() { return CheckNext(); }
    public static string String() { return Next(); }
    public static int Int() { return int.Parse(Next()); }
    public static long Long() { return long.Parse(Next()); }
    public static double Double() { return double.Parse(Next()); }
    public static int[] IntLine() { return Array.ConvertAll(Split(Line()), int.Parse); }
    public static int[] IntArray(int N) { return Range(N, Int); }
    public static int[][] IntTable(int H) { return Range(H, IntLine); }
    public static string[] StringArray(int N) { return Range(N, Next); }
    public static string[][] StringTable(int N) { return Range(N, () => Split(Line())); }
    public static string Line() { return reader.ReadLine().Trim(); }
    public static T[] Range<T>(int N, Func<T> f) { var r = new T[N]; for (int i = 0; i < N; r[i++] = f()) ; return r; }
    static string[] Split(string s) { return s.Split(separator, op); }
    static string Next() { CheckNext(); return A[i++]; }
    static bool CheckNext()
    {
        if (i < A.Length) return true;
        string line = reader.ReadLine();
        if (line == null) return false;
        if (line == "") return CheckNext();
        A = Split(line);
        i = 0;
        return true;
    }
}

Measured memory_bytes: 80680