#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<string>
#include<vector>
#include<map>
#include<set>
#include<list>
#include<queue>
#include<deque>
#include<algorithm>
#include<numeric>
#include<utility>
#include<complex>
#include<functional>
 
using namespace std;

/* constant */

const int MAX_N = 200000;
const int MAX_Q = 200000;
const int MAX_E2 = 1 << 19;

//#define DEBUG(f)	f
#define DEBUG(f)	

const long long INF = 1LL << 60;

/* typedef */

typedef long long ll;

struct Treap {
  struct Node {
    ll key, minkey;
    int fix, size;
    Node *left, *right;

    void print(int k) {
      printf("%d: key=%lld, minkey=%lld, fix=%d, size=%d\n",
	     k, key, minkey, fix, size);
    }
    void print() { print(0); }
  };
  typedef Node *node;

  node root, nullnode, buf;
  Node buf0[MAX_N + MAX_Q + 1], buf1;
  
  Treap() {
    nullnode = &buf1;
    nullnode->left = nullnode->right = nullnode;
    nullnode->key = nullnode->minkey = INF;
    nullnode->fix = nullnode->size = 0;
    clear();
  }

  void clear() { buf = buf0; root = nullnode; }
  
  int size() { return root->size; }

  void update(node& t) {
    t->size = 1 + t->left->size + t->right->size;
    t->minkey = t->key;
    if (t->minkey > t->left->minkey) t->minkey = t->left->minkey;
    if (t->minkey > t->right->minkey) t->minkey = t->right->minkey;
  }

  node gen_node(ll x) {
    node t = buf++;
    t->left = t->right = nullnode;
    t->key = t->minkey = x;
    t->fix = rand();
    t->size = 1;
    return t;
  }
  
  void rot_l(node& k1) {
    node k2 = k1->right;
    k1->right = k2->left;
    k2->left = k1;
    update(k1);
    update(k2);
    k1 = k2;
  }

  void rot_r(node& k1) {
    node k2 = k1->left;
    k1->left = k2->right;
    k2->right = k1;
    update(k1);
    update(k2);
    k1 = k2;
  }

  void append(node& t, ll x) {
    if (t == nullnode)
      t = gen_node(x);
    else {
      append(t->right, x);
      update(t);
      if (t->right->fix > t->fix) rot_l(t);
    }
  }
  void append(ll x) { append(root, x); }
  
  void insertat(node&t, int i, ll x) {
    if (t == nullnode)
      t = gen_node(x);
    else {
      int lsize = t->left->size;
      if (i == lsize) {
	append(t->left, x);
	update(t);
	if (t->left->fix > t->fix) rot_r(t);
      }
      else if (i < lsize) {
	insertat(t->left, i, x);
	update(t);
	if (t->left->fix > t->fix) rot_r(t);
      }
      else {
	insertat(t->right, i - (lsize + 1), x);
	update(t);
	if (t->right->fix > t->fix) rot_l(t);
      }
    }
  }
  void insertat(int i, ll x) { insertat(root, i, x); }

  void set(node& t, int i, ll x) {
    if (t != nullnode) {
      int lsize = t->left->size;
      if (i == lsize) {
	t->key = t->minkey = x;
	update(t);
      }
      else if (i < lsize) {
	set(t->left, i, x);
	update(t);
      }
      else {
	set(t->right, i - (lsize + 1), x);
	update(t);
      }
    }
  }
  void set(int i, ll x) { set(root, i, x); }
  
  void remove_node(node& t) {
    if (t == nullnode) return;
    if (t->left == nullnode || t->right == nullnode) {
      if (t->left == nullnode)
	t = t->right;
      else
	t = t->left;
    }
    else {
      if (t->left->fix < t->right->fix) {
	rot_l(t);
	remove_node(t->left);
	update(t);
      }
      else {
	rot_r(t);
	remove_node(t->right);
	update(t);
      }
    }
  }

  ll removeat(node& t, int i) {
    ll ret = INF;

    if (t != nullnode) {
      int lsize = t->left->size;
      if (i == lsize) {
	ret = t->key;
	remove_node(t);
      }
      else if (i < lsize) {
	ret = removeat(t->left, i);
	update(t);
      }
      else {
	ret = removeat(t->right, i - (lsize + 1));
	update(t);
      }
    }

    return ret;
  }
  ll removeat(int i) { return removeat(root, i); }
  
  int remove_range(node& t, int r0, int r1) {
    if (t == nullnode) return 0;
    if (r1 <= 0 || t->size <= r0) return 0;
    
    if (r0 <= 0 && t->size <= r1) {
      int n = t->size;
      t = nullnode;
      return n;
    }

    int lsize = t->left->size;
    int n =
      remove_range(t->left, r0, r1) +
      remove_range(t->right, r0 - (lsize + 1), r1 - (lsize + 1));
    t->size = 1 + t->left->size + t->right->size;

    if (r0 <= lsize && lsize < r1) {
      n++;
      remove_node(t);
    }

    return n;
  }
  int remove_range(int r0, int r1) { return remove_range(root, r0, r1); }

  ll min_range(node& t, int r0, int r1) {
    if (t == nullnode) return INF;
    if (r1 <= 0 || t->size <= r0) return INF;
    
    if (r0 <= 0 && t->size <= r1) return t->minkey;

    int lsize = t->left->size;
    ll minval = INF;

    if (r0 <= lsize && lsize < r1)
      minval = t->key;
    if (r0 <= lsize) {
      ll minl = min_range(t->left, r0, r1);
      if (minval > minl) minval = minl;
    }
    if (lsize + 1 < r1) {
      ll minr = min_range(t->right, r0 - (lsize + 1), r1 - (lsize + 1));
      if (minval > minr) minval = minr;
    }

    return minval;
  }
  ll min_range(int r0, int r1) { return min_range(root, r0, r1); }

  node find(ll x) {
    node t = root;
    while (t != nullnode) {
      if (t->key == x) return t;
      if (x < t->key) t = t->left;
      else t = t->right;
    }
    return (node)NULL;
  }

  ll findat(int i) {
    node t = root;
    while (t != nullnode) {
      int lsize = t->left->size;
      if (i == lsize) return t->key;
      if (i < lsize)
	t = t->left;
      else {
	i -= lsize + 1;
	t = t->right;
      }
    }
    return -1;
  }

  int upper_bound(ll x) {
    node t = root;
    int k = 0;

    while (t != nullnode) {
      if (t->key <= x) {
	k += t->left->size + 1;
	t = t->right;
      }
      else {
	t = t->left;
      }
    }
    return k;
  }

  
  void print(node t, int k, int indent) {
    if (t == nullnode) return;
    if (t != NULL) {
      for (int i = 0; i < indent; i++) cout << "  ";
      t->print(k + t->left->size);
      print(t->left, k, indent + 1);
      print(t->right, k + t->left->size + 1, indent + 1);
    }
  }
  void print() { print(root, 0, 0); }

  void inorder(node t) {
    if (t != nullnode && t != NULL) {
      inorder(t->left);
      cout << t->key << ' ';
      inorder(t->right);
    }
  }
  void inorder() {
    inorder(root);
    cout << endl;
  }
};

/* global variables */

int n, q;
Treap trp;

/* subroutines */

/* main */

int main() {
  srand(time(NULL));

  cin >> n >> q;

  trp.clear();

  for (int i = 0; i < n; i++) {
    ll a;
    cin >> a;
    trp.append(a);
  }
  DEBUG(trp.print());

  while (q--) {
    int qi, l, r, pos;
    ll val;
    cin >> qi;

    if (qi == 0) {
      cin >> l >> r;
      DEBUG(printf("shift: l=%d, r=%d\n", l, r));
      ll x = trp.removeat(r);
      DEBUG(trp.print());
      DEBUG(trp.inorder());
      trp.insertat(l, x);
      DEBUG(trp.print());
      DEBUG(trp.inorder());
    }
    else if (qi == 1) {
      cin >> l >> r;
      DEBUG(printf("min_range: l=%d, r=%d\n", l, r));
      DEBUG(trp.inorder());
      ll x = trp.min_range(l, r + 1);
      cout << x << endl;
    }
    else {
      cin >> pos >> val;
      DEBUG(printf("set: pos=%d, val=%lld\n", pos, val));
      trp.set(pos, val);
      DEBUG(trp.inorder());
    }
  }
  
  return 0;
}

Measured memory_bytes: 16808