#include <bits/stdc++.h>  // include all standard C++ libraries

using namespace std;

// Loops
#define _overload3(_1,_2,_3,name,...) name
#define _rep(i,n) repi(i,0,n)
#define repi(i,a,b) for(int i=int(a);i<int(b);++i)
#define _rrep(i,n) rrepi(i,n,0)
#define rrepi(i,a,b) for(int i=int(a)-1;i>=int(b);--i)
#define rep(...) _overload3(__VA_ARGS__,repi,_rep,)(__VA_ARGS__)
#define rrep(...) _overload3(__VA_ARGS__,rrepi,_rrep,)(__VA_ARGS__)
#define each(xi, x) for (auto&& xi : x)
// Note: we can use rep(i,N) or rep(i,from,to)


// typedef
using ll = long long;
template <class T> using vec = vector<T>;
using vi   = vector<int>;
using vvi  = vector<vi>;
using vvvi = vector<vvi>;
using pii  = pair<int, int>;


// Constants

// Shorter repr for frequently used terms
#define pb push_back
#define eb emplace_back
#define mp make_pair

// Algorithms
#define all(x) (x).begin(), (x).end()
template<class T> pair<T, size_t> max(vector<T> &x){ auto it = max_element(all(x)); return mp(*it, it-x.begin()); }
template<class T> pair<T, size_t> min(vector<T> &x){ auto it = min_element(all(x)); return mp(*it, it-x.begin()); }
template<class T> inline bool chmax(T &maxval, const T &newval) { if (maxval<newval) { maxval=newval; return 1; } return 0; }
template<class T> inline bool chmin(T &minval, const T &newval) { if (minval>newval) { minval=newval; return 1; } return 0; }

// Utilities
// Grid world utilities
int dx[4]={0,1,0,-1};
int dy[4]={1,0,-1,0};
#define inside(H,W,y,x) 0<=(x) && (x)<(W) && 0<=(y) && (y)<(H)

inline int in() {int x; cin >> x; return x;} // read int from cin
inline  ll IN() {ll x; cin >> x; return x;}  // read ll from cin

// Debug
#ifdef LOCAL
  #include "dump.hpp"
  #define debug(x) cerr << #x << ": " << x << '\n'
#else
  #define dump(...)
  #define debug(x)
#endif

// Paste snippets here!!
struct RMQ {
  vi a; int n;
  RMQ(int n_) {
    int init_val = numeric_limits<int>::max(); // セグ木の各ノードの初期値

    int n = 1;
    while(n<n_) n*=2;
    a.resize(2*(this->n = n)-1, init_val);
  }
  void update(int i, int x){
    i += n-1;  // 葉ノード id に変換
    a[i] = x;  // 葉を更新
    while(i>0){
      i = (i-1)/2;  // 親ノードの id
      a[i] = min(a[2*i+1],a[2*i+2]);  // ２つの子ノードの値を取得
    }
  }
  int query(int x, int y){  // [x,y)
    return query(x,y,0,0,n);
  }
  int query(int x, int y, int k, int l, int r){
    // [x,y) と [l,r) に共通部分がない場合
    if(r<=x || y<=l) return numeric_limits<int>::max();
    // [x,y) が [l,r) を完全に含んでいる場合
    else if(x<=l && r<=y) return a[k];
    // それ以外の場合
    else {
      int vl = query(x,y, 2*k+1, l,       (l+r)/2);
      int vr = query(x,y, 2*k+2, (l+r)/2,       r);
      return min(vl,vr);
    }
  }
};
#include <bits/stdc++.h>
using namespace std;


struct SegTree {
  using T = int;  // 配列のデータ型
  vector<T> dat;
  int n;
  T default_value;

  struct Interval {
    // セグ木の範囲のためのクラス
    int node_id, left, right;  // id はノードID, [left, right)
    inline bool intersects(Interval &other) const { return ! ((right<=other.left) || (other.right<=left)); }
    inline bool includes(Interval &other) const { return (left<=other.left) && (other.right<=right); }
    inline Interval left_child() const { return {2*node_id+1, left, (left+right)/2 }; };
    inline Interval right_child() const { return {2*node_id+2, (left+right)/2, right }; };
    inline Interval parent() const {
      assert(!is_root());
      bool is_left = (node_id%2==1);
      if(is_left) return { (node_id-1)/2, left, 2*right-left };
      else return { (node_id-1)/2, 2*left-right, right };
    }
    inline bool is_root() const { return node_id<=0; }
  };


  // 実装すべきもの
  void update(int i, T val){
    // i番目の要素にvalを適用する
    Interval node = get_node(i);
    (*this)[node] = val;  // RMQ の場合
    while(!node.is_root()){
      node = node.parent();
      (*this)[node] = min((*this)[node.left_child()], (*this)[node.right_child()]); // RMQ の場合
    }
  }
  T query(int left, int right){
    Interval q = {-1, left, right};
    dump(left, right, dat);
    return query(q, root());
  }
  T query(Interval &q, Interval i){
    dump(i.node_id, i.left, i.right);
    if(!q.intersects(i)) return default_value;  // RMQ の場合
    else if(q.includes(i)) return (*this)[i];   // RMQ の場合
    else {
      dump(dat);
      T vl = query(q, i.left_child());  // RMQ
      T vr = query(q, i.right_child()); // RMQ
      dump(vl,vr);
      return min(vl, vr);               // RMQ
    }
  }

  SegTree(int n_, T default_value) : default_value(default_value) {
    // n_: 配列サイズ
    // default_value: 配列の初期値
    init(n_, default_value);
  }
protected:
  // 根を取得
  Interval root() const { return {0, 0, n}; }
  // 配列の i 番目の要素に対応する葉ノードを得る (範囲の大きさは1)
  Interval get_node(int i) const { return {i+n-1, i, i+1}; }
  // 範囲に対応する値への参照を返す
  T& operator[](const Interval &lr) { return dat[lr.node_id]; }
  // 木ノードの移動関連 (通常は Interval クラスを使えば十分なので使わない)
  inline int parent(int node) const { return (node-1)/2; }
  inline int left_child(int node) const { return 2*node+1; }  // おアノード
  inline int right_child(int node) const { return 2*node+2; }
  inline int is_root(int node) const { return node<=0; }

  // 木の初期化
  void init(int n_, T default_value){
    int n=1; while(n<n_) n*=2; dat.resize(2*n-1, default_value); this->n = n;
  }
};
// 

int main(){
  cin.tie(0); ios::sync_with_stdio(false);    // Magic for faster cin
  int n, q; cin>>n>>q;
  int c, x, y;
  SegTree rmq(n, numeric_limits<int>::max());
  rep(i,q){
    cin >> c >> x >> y;
    if(c==0){
      rmq.update(x,y);
    } else {
      cout << rmq.query(x,y+1) << endl;
    }
  }
  return 0;
}



Measured memory_bytes: 3884