#[allow(unused_imports)]
use std::io::{stdin, Read, StdinLock};
#[allow(unused_imports)]
use std::cmp::{max, min, Ordering};
#[allow(unused_imports)]
use std::str::FromStr;
#[allow(unused_imports)]
use std::collections::{HashSet, HashMap, BinaryHeap};
#[allow(unused_imports)]
use std::vec::Vec;

#[allow(dead_code)]
const INF: i32 = 1000_000_000;
#[allow(dead_code)]
const INFLL: i64 = 1000_000_000_000_000_000;
#[allow(dead_code)]
const EPS: f64 = 1.0e-10;
#[allow(dead_code)]
const MOD: i32 = 1000_000_007;

struct Scanner<'a> {
    cin: StdinLock<'a>,
}

impl<'a> Scanner<'a> {
    #[allow(dead_code)]
    fn new(cin: StdinLock<'a>) -> Scanner<'a> {
        Scanner {cin: cin}
    }

    #[allow(dead_code)]
    fn read1<T: FromStr>(&mut self) -> Option<T> {
        let token = self.cin.by_ref().bytes().map(|c| c.unwrap() as char)
                    .skip_while(|c| c.is_whitespace())
                    .take_while(|c| !c.is_whitespace())
                    .collect::<String>();
        token.parse::<T>().ok()
    }

    #[allow(dead_code)]
    fn read<T: FromStr>(&mut self) -> T {
        self.read1().unwrap()
    }
}

#[allow(dead_code)]
trait Monoid {
    /// data type
    type T: Clone;

    /// Identity function
    fn id() -> Self::T;

    /// operator. op(a, b) <-> ab in this order
    fn op(a: &Self::T, b: &Self::T) -> Self::T;
}

/// example: Range Minimum Query
struct RMQ;
impl Monoid for RMQ {
    type T = i32;

    fn id() -> i32 {
        i32::max_value()
    }

    fn op(a: &i32, b: &i32) -> i32 {
        min(*a, *b)
    }
}

/// helper
fn expand(n: usize) -> usize {
    assert!(n >= 1);
    match n {
        1 => 1,
        n => expand((n + 1) / 2) * 2,
    }
}

struct SegmentTree<M: Monoid> {
    data: Vec<M::T>,
    size: usize,
    n: usize,
}

#[allow(dead_code)]
impl<M: Monoid> SegmentTree<M> {
    fn new(n: usize) -> SegmentTree<M> {
        let sz = n;
        let n = expand(sz);
        SegmentTree::<M> {
            data: vec![M::id(); n * 2],
            size: sz,
            n: n,
        }
    }
    fn new_from_vec(data: &Vec<M::T>) -> SegmentTree<M> {
        let sz = data.len();
        let n = expand(sz);
        let mut dat: Vec<M::T> = vec![M::id(); n * 2];
        for i in 0..sz {
            dat[n + i] = data[i].clone();
        }
        for i in (1..n).rev() {
            dat[i] = M::op(&dat[i * 2], &dat[i * 2 + 1]);
        }

        SegmentTree::<M> {
            data: dat,
            size: sz,
            n: n,
        }
    }

    fn update(&mut self, mut p: usize, val: M::T) {
        assert!(p < self.size);
        p += self.n;
        self.data[p] = val;
        p /= 2;
        while p > 0 {
            self.data[p] = M::op(&self.data[p * 2], &self.data[p * 2 + 1]);
            p /= 2;
        }
    }

    fn query(&self, mut l: usize, mut r: usize) -> M::T {
        assert!(l < r && r <= self.size);
        let n = self.n;
        l += n; r += n;
        let mut res1 = M::id();
        let mut res2 = M::id();
        while l != r {
            if l & 1 > 0 {
                res1 = M::op(&res1, &self.data[l]);
                l += 1;
            }
            if r & 1 > 0 {
                r -= 1;
                res2  = M::op(&self.data[r], &res2);
            }
            l /= 2; r /= 2;
        }
        M::op(&res1, &res2)
    }

    fn size(&self) -> usize {
        self.size
    }
}

fn main() {
    let cin = stdin();
    let cin = cin.lock();
    let mut sc = Scanner::new(cin);
    let n: usize = sc.read();
    let q: usize = sc.read();
    let mut seg: SegmentTree<RMQ> = SegmentTree::new(n);
    for _i in 0..q {
        let com: i32 = sc.read();
        let x: usize = sc.read();
        let y: usize = sc.read();
        match com {
            0 => seg.update(x, y as i32),
            1 => println!("{}", seg.query(x, y + 1)),
            _ => panic!("Invalid query")
        }
    }
}


Measured memory_bytes: 5048