Go: package main

// packed from [splaytree.go main.rmq.go splaytree_debug.go] with goone.

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"strconv"
	"strings"
)

type SplayTreeNode struct {
	parent, left, right *SplayTreeNode

	size    int
	value   int
	minimum int
}

func newSplayTreeNode(v int) *SplayTreeNode {
	return &SplayTreeNode{value: v}
}

func (node *SplayTreeNode) rotate() {
	var pp, p, c *SplayTreeNode
	p = node.parent
	pp = p.parent
	if pp != nil && pp.left == p {
		pp.left = node
	}
	if pp != nil && pp.right == p {
		pp.right = node
	}
	node.parent = pp
	if p.left == node {
		c = node.right
		node.right = p
		p.left = c
	} else {
		c = node.left
		node.left = p
		p.right = c
	}
	p.parent = node
	if c != nil {
		c.parent = p
	}
	p.update()
	node.update()
}

func (node *SplayTreeNode) state() int {
	if node.parent == nil {
		return 0
	}
	if node.parent.left == node {
		return -1
	} else if node.parent.right == node {
		return 1
	}
	return 0
}

func (node *SplayTreeNode) splay() {
	for node.state() != 0 {
		if node.parent.state() == 0 {
			node.rotate()
			return
		}
		if node.state() == node.parent.state() {
			node.parent.rotate()
			node.rotate()
		} else {
			node.rotate()
			node.rotate()
		}
	}
}

func (node *SplayTreeNode) update() {
	node.size = 1
	node.minimum = node.value
	if node.left != nil {
		node.size += node.left.size
		node.minimum = min(node.minimum, node.left.minimum)
	}
	if node.right != nil {
		node.size += node.right.size
		node.minimum = min(node.minimum, node.right.minimum)
	}
}
func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

func (node *SplayTreeNode) Get(idx int) *SplayTreeNode {
	cur := node
	for true {
		lSize := 0
		if cur.left != nil {
			lSize = cur.left.size
		}
		if idx < lSize {
			cur = cur.left
		}
		if lSize == idx {
			cur.splay()
			return cur
		}
		if idx > lSize {
			cur = cur.right
			idx = idx - lSize - 1
		}
	}
	return nil
}

func mergeSplayNode(l, r *SplayTreeNode) (root *SplayTreeNode) {
	if l == nil {
		return r
	}
	if r == nil {
		return l
	}
	root = l.Get(l.size - 1)
	root.right = r
	r.parent = root
	root.update()
	return root
}

func (node *SplayTreeNode) Split(leftCount int) (left *SplayTreeNode, right *SplayTreeNode) {
	root := node.Get(leftCount)
	lRoot, rRoot := root.left, root

	rRoot.left = nil
	if lRoot != nil {
		lRoot.parent = nil
	}
	rRoot.update()
	return lRoot, rRoot
}

func splayTreeInsert(t *SplayTreeNode, idx int, node *SplayTreeNode) (root *SplayTreeNode) {
	l, r := t.Split(idx)
	return mergeSplayNode(
		mergeSplayNode(l, node),
		r,
	)
}

func removeSplayTree(t *SplayTreeNode, idx int) (root *SplayTreeNode, removed *SplayTreeNode) {
	target := t.Get(idx)
	l, r := target.left, target.right
	if l != nil {
		l.parent = nil
	}
	if r != nil {
		r.parent = nil
	}
	target.left = nil
	target.right = nil
	target.update()
	return mergeSplayNode(l, r), target
}

const (
	initialBufSize = 100000
	maxBufSize     = 1000000
)

var sc *bufio.Scanner

func initScanner(r io.Reader) *bufio.Scanner {
	buf := make([]byte, initialBufSize)

	sc := bufio.NewScanner(r)
	sc.Buffer(buf, maxBufSize)
	sc.Split(bufio.ScanWords)
	return sc
}

func main() {
	sc = initScanner(os.Stdin)
	resolve()
}

func resolve() {
	n := scanInt(sc)
	q := scanInt(sc)
	table := newDebugNodeSlice(200001)
	for i := 0; i < n; i++ {
		table.l[i].parent = &table.l[i+1]
		table.l[i+1].left = &table.l[i]
		table.l[i].update()
		table.l[i+1].update()
	}
	var root *SplayTreeNode = &table.l[n]
	for i := 0; i < n; i++ {
		table.l[i].value = scanInt(sc)
		table.l[i].update()
	}
	table.l[n].update()
	for i := 0; i < q; i++ {
		switch scanInt(sc) {
		case 0:
			root = shift(root, scanInt(sc), scanInt(sc))
		case 1:
			l := scanInt(sc)
			r := scanInt(sc)
			tmp, rRoot := root.Split(r + 1)
			lRoot, cRoot := tmp.Split(l)
			fmt.Println(cRoot.minimum)
			root = mergeSplayNode(mergeSplayNode(lRoot, cRoot), rRoot)
		case 2:
			root = root.Get(scanInt(sc))
			root.value = scanInt(sc)
			root.update()
		}
	}
}


C++: #include <algorithm>
#include <array>
#include <cassert>
#include <cmath>
#include <iomanip>
#include <iostream>
#include <limits>
#include <map>
#include <numeric>
#include <queue>
#include <random>
#include <memory>
#include <sstream>
#include <stack>
#include <string>
#include <utility>
#include <vector>
#define FOR(i,a,b) for (int i=(a),for_##i##_max=(b);i<=(for_##i##_max);++i)
#define RFOR(i,a,b) for (int i=(a),rfor_##i##_min=(b);i>=(rfor_##i##_min);--i)
#define REP(i,n) for (int i=0,rep_##i##_len=(n);i<(rep_##i##_len);++i)
#define RREP(i,n) for (int i=(n)-1;i>=0;--i)

namespace { // Basics

template<typename T>
void chmin(T &x, T y) {
  x = std::min(x, y);
}

template<typename T>
void chmax(T &x, T y) {
  x = std::max(x, y);
}

template<typename T>
__attribute__((unused))
const auto minimum = static_cast<T const &(*)(T const &, T const &)>(std::min);

template<typename T>
__attribute__((unused))
const auto maximum = static_cast<T const &(*)(T const &, T const &)>(std::max);

template<typename T>
__attribute__((unused))
T constexpr infty = std::numeric_limits<T>::max / 3;
template<>
__attribute__((unused))
auto constexpr infty<int> = 1'100'100'100;
template<>
__attribute__((unused))
auto constexpr infty<long long int> = 100'500'400'300'200'100LL;

using lli = long long int;
using ld = long double;

} // namespace

namespace  { // Implicit Treap

template <typename T, typename FnT, typename Serv>
class itreap {
  public:
    struct node_t {
      using ptr_t = std::unique_ptr<node_t>;
      T key; double prio; ptr_t lc, rc; size_t cnt; T sum;
      explicit node_t(T v, double p): key(v), prio(p), lc(), rc(), cnt(1), sum(v) {}
      static size_t count(node_t *t) { return t? t->cnt : 0; }
      static size_t total(node_t *t, T unit) { return t? t->sum : unit; }
      T at(size_t idx) const {
        auto const ls = count(lc.get());
        if (idx == ls) { return key; }
        return (idx < ls)? lc->at(idx) : rc->at(idx - ls - 1);
      }
      void recompute(FnT const &append, T unit) {
        cnt = count(lc.get()) + 1 + count(rc.get());
        auto const lv = total(lc.get(), unit);
        auto const rv = total(rc.get(), unit);
        sum = append(lv, append(key, rv));
      }
      static std::pair<ptr_t, ptr_t> split(ptr_t t, size_t k, FnT const &app, T unit) {
        if (!t) { return std::make_pair(nullptr, nullptr); }
        if (k <= count(t->lc.get())) {
          auto s = split(std::move(t->lc), k, app, unit);
          t->lc = std::move(s.second); t->recompute(app, unit);
          return std::make_pair(std::move(s.first), std::move(t));
        } else {
          auto s = split(std::move(t->rc), k - count(t->lc.get()) - 1, app, unit);
          t->rc = std::move(s.first); t->recompute(app, unit);
          return std::make_pair(std::move(t), std::move(s.second));
        }
      }
      static ptr_t merge(ptr_t l, ptr_t r, FnT const &app, T unit) {
        if (!l || !r) { return std::move(l? l : r); }
        if (l->prio > r->prio) {
          l->rc = merge(std::move(l->rc), std::move(r), app, unit);
          l->recompute(app, unit);
          return l;
        } else {
          r->lc = merge(std::move(l), std::move(r->lc), app, unit);
          r->recompute(app, unit);
          return r;
        }
      }
    };
    using ptr_t = typename node_t::ptr_t;

    explicit itreap(std::shared_ptr<Serv> s, size_t N, T v): srv(s), root() {
      for (size_t i = 0; i < N; ++i) {
        push_back(v);
      }
    }
    explicit itreap(std::shared_ptr<Serv> s, ptr_t &&r): srv(s), root(std::move(r)) {}
    T const operator[] (size_t idx) const {
      return root->at(idx);
    }
    void update(size_t idx, T v) {
      auto right = eliminate(idx+1);
      eliminate(idx);
      push_back(v);
      merge(std::move(right));
    }
    void merge(itreap &&right) {
      root = node_t::merge(std::move(root), std::move(right.root), srv->get_op(), srv->get_unit());
    }
    void push_back(T v) {
      auto const p = srv->generate_priority();
      root = node_t::merge(std::move(root), std::make_unique<node_t>(v, p),
          srv->get_op(), srv->get_unit());
    }
    itreap eliminate(size_t N) {
      auto s = node_t::split(std::move(root), N, srv->get_op(), srv->get_unit());
      root = std::move(s.first);
      return itreap(srv, std::move(s.second));
    }
    void insert(size_t idx, T v) {
      auto const right = eliminate(idx);
      push_back(v);
      merge(std::move(right));
    }
    void erase(size_t idx) {
      auto right = eliminate(idx);
      merge(right.eliminate(1));
    }
    T query(size_t first, size_t last) {
      if (first == last) { return 0; }
      auto right = eliminate(last);
      auto mid = eliminate(first);
      T ans = mid.root->sum;
      merge(std::move(mid));
      merge(std::move(right));
      return ans;
    }
  private:
    std::shared_ptr<Serv> srv;
    ptr_t root;
};

template <typename T, typename FnT>
class itreap_server: public std::enable_shared_from_this<itreap_server<T, FnT>> {
  public:
    using Seq = itreap<T, FnT, itreap_server>;
    static std::shared_ptr<itreap_server> create(FnT fn, T e) {
      return std::make_shared<itreap_server>(fn, e);
    }
    explicit itreap_server(FnT fn, T e):
      mt(std::random_device()()), dis(0, 1), append(fn), unit(e) { }
    Seq make_seq(size_t N, T v) {
      return Seq(this->shared_from_this(), N, v);
    }
    double generate_priority() {
      return dis(mt);
    }
    FnT const &get_op() const { return append; }
    T get_unit() const { return unit; }
  private:
    std::mt19937 mt;
    std::uniform_real_distribution<double> dis;
    FnT append;
    T unit;
};

} // namespace

using namespace std;

using Serv = itreap_server<lli, lli const &(*)(lli const &, lli const &)>;

int main() {
  int N, Q; cin >> N >> Q;
  auto srv = Serv::create(static_cast<lli const &(*)(lli const &, lli const &)>(std::min), infty<lli>);
  auto seq = srv->make_seq(N+10, 0);

  REP(i, N) {
    int A; cin >> A;
    seq.update(i, A);
  }
  REP(query, Q) {
    int X; cin >> X;
    if (X == 0) {
      int L, R; cin >> L >> R;
      auto d = seq.eliminate(R+1);
      auto c = seq.eliminate(R);
      auto b = seq.eliminate(L);
      seq.merge(std::move(c));
      seq.merge(std::move(b));
      seq.merge(std::move(d));
    } else if (X == 1) {
      int L, R; cin >> L >> R;
      cout << seq.query(L, R+1) << endl;
    } else {
      int P, V; cin >> P >> V;
      seq.update(P, V);
    }
  }
}