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# (judge verdict: Accepted)

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 Treap();
        for (int i = 0; i < N; i++) tree.Insert(i, A[i]);

        foreach (var q in Reader.IntTable(Q))
            if (q[0] == 0)
            {
                // b1, b2 -> b2, b1
                int L = q[1], R = q[2] + 1;
                Treap.Node a, b, b1, b2, c, t;
                Treap.Split(tree.Root, R, out t, out c);
                Treap.Split(t, L, out a, out b);
                Treap.Split(b, R - L - 1, out b1, out b2);
                tree.Root = Treap.Merge(a, b2);
                tree.Root = Treap.Merge(tree.Root, b1);
                tree.Root = Treap.Merge(tree.Root, c);
            }
            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 Treap
    {
        public class Node
        {
            public Node L, R;
            public int Count;
            public long Val;
            public long Min;
            //public long Max;
            //public long Sum;
            public bool Reversed;
            public readonly uint Priority;
            static readonly XorShift random = new XorShift();

            public Node(long val)
            {
                Val = val; Count = 1; Priority = random.Next();
                Min = val;
                //Max = val;
                //Sum = val;
            }
        }
        public class XorShift
        {
            uint m = 2463534242;
            public uint Next() { m = m ^ m << 13; m = m ^ m >> 17; return m = m ^ m << 5; }
        }

        public Node Root;
        public int Count { get { return Root == null ? 0 : Root.Count; } }
        public static int Size(Node node) { return node == null ? 0 : node.Count; }
        static long Min(Node node) { return node == null ? long.MaxValue : node.Min; }
        //static long Max(Node node) { return node == null ? long.MinValue : node.Max; }
        //static long Sum(Node node) { return node == null ? 0 : node.Sum; }

        Node GenNode(long val) { return new Node(val); }

        //[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        static Node Update(Node node)
        {
            node.Count = 1 + Size(node.L) + Size(node.R);
            node.Min = Math.Min(node.Val, Math.Min(Min(node.L), Min(node.R)));
            //node.Max = Math.Max(node.Val, Math.Max(Max(node.L), Max(node.R)));
            //node.Sum = node.Val + Sum(node.L) + Sum(node.R);
            return node;
        }

        public long this[int i]
        {
            get { return GetRec(Root, i); }
            set { SetRec(Root, i, value); }
        }
        long GetRec(Node node, int index)
        {
            Debug.Assert(node != null);
            if (node.Reversed) PushDownReversed(node);
            int Lsize = Size(node.L);
            if (index == Lsize) return node.Val;
            if (index < Lsize) return GetRec(node.L, index);
            return GetRec(node.R, index - Lsize - 1);
        }
        void SetRec(Node node, int index, long val)
        {
            Debug.Assert(node != null);
            if (node.Reversed) PushDownReversed(node);
            int Lsize = Size(node.L);
            if (index == Lsize) node.Val = val;
            else if (index < Lsize) SetRec(node.L, index, val);
            else SetRec(node.R, index - Lsize - 1, val);
            Update(node);
        }
        public void AddVal(int index, long val) { AddVal(Root, index, val); }
        void AddVal(Node node, int index, long val)
        {
            if (node.Reversed) PushDownReversed(node);
            int Lsize = Size(node.L);
            if (index == Lsize) node.Val += val;
            else if (index < Lsize) AddVal(node.L, index, val);
            else AddVal(node.R, index - Lsize - 1, val);
            Update(node);
        }

        public static Node Merge(Node L, Node R)
        {
            if (L == null) return R;
            if (R == null) return L;
            if (L.Reversed) PushDownReversed(L);
            if (R.Reversed) PushDownReversed(R);

            if (L.Priority > R.Priority)
            {
                L.R = Merge(L.R, R);
                return Update(L);
            }
            else
            {
                R.L = Merge(L, R.L);
                return Update(R);
            }
        }

        public static void Split(Node node, int index, out Node L, out Node R)
        {
            if (node == null) { L = R = null; return; }
            if (node.Reversed) PushDownReversed(node);
            int Lsize = Size(node.L);
            if (index == Lsize)
            {
                L = node.L;
                R = node;
                node.L = null;
                Update(node);
                return;
            }
            Node a, b;
            if (index < Lsize)
            {
                Split(node.L, index, out a, out b);
                node.L = b;
                L = a;
                R = Update(node);
            }
            else
            {
                Split(node.R, index - Lsize - 1, out a, out b);
                node.R = a;
                L = Update(node);
                R = b;
            }
        }
        public static void Split(Node node, int L, int R, out Node a, out Node b, out Node c)
        {
            Node t;
            Split(node, R, out t, out c);
            Split(t, L, out a, out b);
        }

        public void Insert(int index, long val) { Root = Insert(Root, GenNode(val), index); }
        Node Insert(Node node, Node newNode, int index)
        {
            if (node == null) return newNode;
            if (node.Reversed) PushDownReversed(node);
            if (newNode.Priority > node.Priority)
            {
                Node L, R;
                Split(node, index, out L, out R);
                return Merge(Merge(L, newNode), R);
            }
            else
            {
                if (index <= Size(node.L))
                    node.L = Insert(node.L, newNode, index);
                else
                    node.R = Insert(node.R, newNode, index - Size(node.L) - 1);
                return Update(node);
            }
        }


        public void RemoveAt(int index) { Root = RemoveAt(Root, index); }
        Node RemoveAt(Node node, int index)
        {
            if (node.Reversed) PushDownReversed(node);
            int Lsize = Size(node.L);
            if (index == Lsize)
                return Merge(node.L, node.R);
            if (index < Lsize)
                node.L = RemoveAt(node.L, index);
            else
                node.R = RemoveAt(node.R, index - Lsize - 1);
            return Update(node);
        }

        public long Min(int L, int R) { return Min(Root, L, R); }
        long Min(Node node, int L, int R)
        {
            if (node == null || L >= R) return long.MaxValue;
            if (L == 0 && R == node.Count) return node.Min;
            if (node.Reversed) PushDownReversed(node);
            long res = long.MaxValue;
            int Lsize = Size(node.L);
            if (Lsize >= L && Lsize < R)
                res = node.Val;
            if (L < Lsize)
                res = Math.Min(res, Min(node.L, L, Math.Min(R, Lsize)));
            if (R - Lsize - 1 > 0)
                res = Math.Min(res, Min(node.R, Math.Max(0, L - Lsize - 1), R - Lsize - 1));
            return res;
        }


        //public long Max(int L, int R) { return RangeQuery(Root, L, R, long.MinValue, n => n.Max, Math.Max); }
        //public long Sum(int L, int R) { return Sum(Root, L, R); }
        //long Sum(Node node, int L, int R)
        //{
        //    if (node == null || L >= R) return 0;
        //    if (L == 0 && R == node.Count) return node.Sum;
        //    if (node.Reversed) PushDownReversed(node);
        //    long res = 0;
        //    int Lsize = Size(node.L);
        //    if (Lsize >= L && Lsize < R)
        //        res = node.Val;
        //    if (L < Lsize)
        //        res += Sum(node.L, L, Math.Min(R, Lsize));
        //    if (R - Lsize - 1 > 0)
        //        res += Sum(node.R, Math.Max(0, L - Lsize - 1), R - Lsize - 1);
        //    return res;
        //}

        public void Reverse(int L = 0, int R = -1) { Root = Reverse(Root, L, R == -1 ? Count : R); }
        public static Node Reverse(Node node, int L = 0, int R = -1)
        {
            if (node == null) return null;
            if (R == -1) R = Size(node);
            Node a, b, c;
            Split(node, L, R, out a, out b, out c);
            b.Reversed ^= true;
            return Merge(a, Merge(b, c));
        }
        static void PushDownReversed(Node node)
        {
            Debug.Assert(node.Reversed);
            if (node.L != null) node.L.Reversed ^= true;
            if (node.R != null) node.R.Reversed ^= true;
            Node t = node.L; node.L = node.R; node.R = t;
            node.Reversed = false;
            Update(node);
        }
    }
}

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(); }
    static string[] Split(string s) { return s.Split(separator, op); }
    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 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;
    }
}