#include <iostream>
#include <algorithm>
#include <numeric>
#include <vector>
#include <cassert>
#include <string>
#include <memory.h>
#include <queue>
#include <cstdio>
#include <cstdlib>
#include <set>
#include <map>
#include <cctype>
#include <iomanip>
#include <sstream>
#include <cctype>
#include <fstream>
#include <cmath>
using namespace std;

#define REP2(i, m, n) for(int i = (int)(m); i < (int)(n); i++)
#define REP(i, n) REP2(i, 0, n)
#define ALL(c) (c).begin(), (c).end()
#define ITER(c) __typeof((c).begin())
#define PB(e) push_back(e)
#define FOREACH(i, c) for(ITER(c) i = (c).begin(); i != (c).end(); ++i)
#define MP(a, b) make_pair(a, b)
#define PARITY(n) ((n) & 1)

typedef long long ll;
typedef pair<ll, ll> P;
const int INF = 1000 * 1000 * 1000 + 7;

class random_bst{
  struct node_t{
    int val;
    int cnt;
    int min;
    int dep;
    
    node_t *chl, *chr;
    node_t(int v) : val(v), cnt(1), min(v), dep(1), chl(NULL), chr(NULL) {}    
    ~node_t(){
      if(this->chl) delete this->chl;
      if(this->chr) delete this->chr;
    }
  };

  node_t *root;
  
  inline int min(node_t *t)  { return t ? t->min   : INF;}
  inline int depth(node_t *t){ return t ? t->dep   : 0;  }
  inline int count(node_t *t){ return t ? t->cnt   : 0;  }
  inline int value(node_t *t){ return t ? t->val   : 0;  }
  
  inline node_t *update(node_t *t){
    t->cnt = count(t->chl) + count(t->chr) + 1;
    t->min = std::min(std::min(min(t->chl), min(t->chr)), t->val);
    t->dep = std::max(depth(t->chl), depth(t->chr)) + 1;
    return t;
  }
  
  node_t *merge(node_t *l, node_t *r){
    if(!l || !r) return l ? l : r;

    if(rand() % (count(l) + count(r)) < count(l)){
      // lを新しい根に
      l->chr = merge(l->chr, r);
      return update(l);
    }else{
      // rを新しい根に
      r->chl = merge(l, r->chl);
      return update(r);
    }
  }
  
  pair<node_t*, node_t*> split(node_t *t, int k){
    if(!t) return make_pair<node_t*, node_t*>(NULL, NULL);
    if(k <= count(t->chl)){
      pair<node_t *, node_t *> s = split(t->chl, k);
      t->chl = s.second;
      return make_pair(s.first, update(t));
    }else{
      pair<node_t *, node_t *> s = split(t->chr, k - count(t->chl) - 1);
      t->chr = s.first;
      return make_pair(update(t), s.second);
    }
  }

public:
  random_bst() : root(NULL) {}
  ~random_bst(){ delete root; }
  
  void erase(int pos){
    pair<node_t*, node_t*> pr = split(root, pos + 1);
    pair<node_t*, node_t*> pl = split(pr.first, pos);
    root = update(merge(pl.first, pr.second));
  }

  void insert(int pos, int val){
    pair<node_t*, node_t*> s = split(root, pos);
    root = update(merge(merge(s.first, new node_t(val)), s.second));
  }
  
  void shift(int l, int r){
    pair<node_t*, node_t*> pr = split(root, r); 
    pair<node_t*, node_t*> pm = split(pr.first, r - 1);
    pair<node_t*, node_t*> pl = split(pm.first, l);
    root = merge(merge(pl.first, pm.second),
                 merge(pl.second,pr.second));
  }
  
  int query(int l, int r){
    pair<node_t*, node_t*> pr = split(root, r);
    pair<node_t*, node_t*> pl = split(pr.first, l);
    int res = min(pl.second);
    root = merge(pl.first, pl.second);
    root = merge(root, pr.second);
    return res;
  }
};

int main(){
  int n, q, x, y, z, a;
  cin >> n >> q;
  random_bst t;
  REP(i, n){
    cin >> a;
    t.insert(i, a);
  }
  
  REP(i, q){
    cin >> x >> y >> z;
    switch(x){
    case 0:
      t.shift(y, z + 1);
      break;
    case 1:
      cout << t.query(y, z + 1) << endl;
      break;
    case 2:
      t.erase(y);
      t.insert(y, z);
      break;
    }
  }
  return 0;
}