Problem:
RMQ and RUQ Write a program which manipulates a sequence A = { a 0 , a 1 , . . .  , a n−1 } with the following operations:  update(s, t, x) : change a s , a s+1 , ..., a t to x .  find(s, t) : report the minimum element in a s , a s+1 , ..., a t .  Note that the initial values of a i ( i = 0, 1, . . .  , n−1 ) are 2 31 -1. Input  n  q  query 1  query 2 : query q  In the first line, n (the number of elements in A ) and q (the number of queries) are given. Then, i th query query i is given in the following format: 0 s  t  x  or 1 s  t  The first digit represents the type of the query. '0' denotes update(s, t, x) and '1' denotes find(s, t) . Output For each find operation, print the minimum value. Constraints   1 ≤ n ≤ 100000    1 ≤ q ≤ 100000    0 ≤ s ≤ t < n    0 ≤ x < 2 31 −1   Sample Input 1 3 5
0 0 1 1
0 1 2 3
0 2 2 2
1 0 2
1 1 2 Sample Output 1 1
2 Sample Input 2 1 3
1 0 0
0 0 0 5
1 0 0 Sample Output 2 2147483647
5

Solution (Python):





import sys

try:
    profile
except:
    def profile(f): return f


def set_depth(depth):
    global DEPTH, SEGTREE_SIZE, NONLEAF_SIZE
    DEPTH = depth
    SEGTREE_SIZE = 1 << DEPTH
    NONLEAF_SIZE = 1 << (DEPTH - 1)


def set_width(width):
    set_depth((width - 1).bit_length() + 1)


@profile
def force_point(value_table, action_table, pos, action_force, action_composite, action_unity):
    action = action_table[pos]
    if action != action_unity:
        value_table[pos] = action

        if pos < NONLEAF_SIZE:
            action_table[pos * 2] = action
            action_table[pos * 2 + 1] = action
    action_table[pos] = action_unity


def force_down_propagate(action_table, value_table, pos, action_composite, action_force, action_unity):
    max_level = pos.bit_length() - 1

    for level in range(max_level):
        i = pos >> (max_level - level)
        action = action_table[i]
        if action != action_unity:
            action_table[i * 2] = action_composite(action, action_table[i * 2])
            action_table[i * 2 +
                         1] = action_composite(action, action_table[i * 2 + 1])
            action_table[i] = action_unity
            value_table[i * 2] = action
            value_table[i * 2 + 1] = action


def down_propagate_target(pos):
    max_level = pos.bit_length() - 1

    for level in range(max_level):
        i = pos >> (max_level - level)
        yield i


@profile
def force_range_update(value_table, action_table, left, right, action, action_force, action_composite, action_unity):
    """
    action_force: action, value, cell_size => new_value
    action_composite: new_action, old_action => composite_action
    """
    left += NONLEAF_SIZE
    right += NONLEAF_SIZE
    while left < right:
        if left & 1:
            value_table[left] = action
            action_table[left] = action
            left += 1
        if right & 1:
            right -= 1
            value_table[right] = action
            action_table[right] = action

        left //= 2
        right //= 2


@profile
def range_reduce(table, left, right, binop, unity):
    ret_left = unity
    ret_right = unity
    left += NONLEAF_SIZE
    right += NONLEAF_SIZE
    while left < right:
        if left & 1:
            ret_left = binop(ret_left, table[left])
            left += 1
        if right & 1:
            right -= 1
            ret_right = binop(table[right], ret_right)

        left //= 2
        right //= 2
    return binop(ret_left, ret_right)


@profile
def up_prop_force(value_table, action_table, pos, binop, action_force, action_composite, action_unity):
    """
    force_children + up_propagation
    """
    while pos > 1:
        pos >>= 1
        force_point(
            value_table, action_table,
            pos * 2, action_force, action_composite, action_unity)

        force_point(
            value_table, action_table,
            pos * 2 + 1, action_force, action_composite, action_unity)
        value_table[pos] = binop(
            value_table[pos * 2],
            value_table[pos * 2 + 1]
        )


def up_propagate(table, pos, binop):
    while pos > 1:
        pos >>= 1
        table[pos] = binop(
            table[pos * 2],
            table[pos * 2 + 1]
        )


def up(pos):
    pos += SEGTREE_SIZE // 2
    return pos // (pos & -pos)


def lazy_range_update(
        action_table, value_table, start, end,
        action, action_composite, action_force, action_unity, value_binop):
    "update [start, end)"
    L = up(start)
    R = up(end)
    force_down_propagate(
        action_table, value_table, L,
        action_composite, action_force, action_unity)
    force_down_propagate(
        action_table, value_table, R,
        action_composite, action_force, action_unity)
    force_range_update(
        value_table, action_table, start, end,
        action, action_force, action_composite, action_unity)
    up_propagate(value_table, L, value_binop)
    up_propagate(value_table, R, value_binop)


def lazy_range_reduce(
    action_table, value_table, start, end,
    action_composite, action_force, action_unity,
    value_binop, value_unity
):
    "reduce [start, end)"

    force_down_propagate(
        action_table, value_table, up(start),
        action_composite, action_force,  action_unity)
    force_down_propagate(
        action_table, value_table, up(end),
        action_composite, action_force, action_unity)

    return range_reduce(value_table, start, end, value_binop, value_unity)


def debugprint(xs, minsize=0, maxsize=None):
    global DEPTH
    strs = [str(x) for x in xs]
    if maxsize != None:
        for i in range(NONLEAF_SIZE, SEGTREE_SIZE):
            strs[i] = strs[i][:maxsize]
    s = max(len(s) for s in strs[NONLEAF_SIZE:])
    if s > minsize:
        minsize = s

    result = ["|"] * DEPTH
    level = 0
    next_level = 2
    for i in range(1, SEGTREE_SIZE):
        if i == next_level:
            level += 1
            next_level *= 2
        width = ((minsize + 1) << (DEPTH - 1 - level)) - 1
        result[level] += strs[i].center(width) + "|"
    print(*result, sep="\n")


def main():
    N, Q = map(int, input().split())
    set_width(N)

    value_unity = (1 << 31) - 1
    value_table = [value_unity] * SEGTREE_SIZE
    action_unity = -1
    action_table = [action_unity] * SEGTREE_SIZE

    action_force = None

    def action_composite(new_action, old_action):
        return new_action

    for _ in range(Q):
        q, *args = map(int, input().split())
        if q == 0:
            
            s, t, value = args
            lazy_range_update(
                action_table, value_table, s, t + 1, value,
                action_composite, action_force, action_unity, min)
        else:
            
            s, t = args
            print(lazy_range_reduce(
                action_table, value_table, s, t + 1,
                action_composite, action_force, action_unity, min, value_unity))
        
        


T1 = """
3 5
0 0 1 1
0 1 2 3
0 2 2 2
1 0 2
1 1 2
"""
TEST_T1 = """
>>> as_input(T1)
>>> main()
1
2
"""
T2 = """
1 3
1 0 0
0 0 0 5
1 0 0
"""
TEST_T2 = """
>>> as_input(T2)
>>> main()
2147483647
5
"""
T3 = """
8 10
0 1 6 5
0 2 7 2
0 2 5 7
1 3 3
1 2 4
1 0 3
1 5 7
1 2 6
0 3 7 9
1 2 6
"""
TEST_T3 = """
>>> as_input(T3)
>>> main()
7
7
5
2
2
7
"""
T4 = """
15 3
0 1 6 5
0 2 7 2
1 6 7
"""
TEST_T4 = """
>>> as_input(T4)
>>> main()
2
"""


def _test():
    import doctest
    doctest.testmod()
    g = globals()
    for k in sorted(g):
        if k.startswith("TEST_"):
            doctest.run_docstring_examples(g[k], g)


def as_input(s):
    "use in test, use given string as input file"
    import io
    global read, input
    f = io.StringIO(s.strip())

    def input():
        return bytes(f.readline(), "ascii")

    def read():
        return bytes(f.read(), "ascii")


if sys.argv[-1] == "-t":
    print("testing")
    _test()
    sys.exit()

main()

Solution (Python):
from typing import Callable, Iterator, List, Optional, Sequence, Union


class LazySegmentTree:
    """Segment Tree with Lazy Propagation"""

    __slots__ = [
        "_identity",
        "_seg_func",
        "_modifying_func",
        "_level",
        "_size",
        "_tree",
        "_lazy",
    ]

    def __init__(
        self,
        initial_values: Sequence,
        identity: Union[int, str],
        seg_func: Callable,
        modifying_func: Optional[Callable] = None,
    ) -> None:
        self._identity = identity
        self._seg_func = seg_func
        self._modifying_func = modifying_func
        self._level = (len(initial_values) - 1).bit_length()
        self._size = 1 << self._level
        self._tree = self._build(initial_values)
        self._lazy: List[Optional[Union[int, str]]] = [None] * (2 * self._size)

    def _build(self, initial_values: Sequence) -> List[Union[int, str]]:
        """Build a segment tree with initial values."""
        tree = [self._identity] * (2 * self._size)

        if self._modifying_func:
            initial_values = map(self._modifying_func, initial_values)

        for idx, val in enumerate(initial_values):  
            tree[idx + self._size - 1] = val

        for idx in range(self._size - 2, -1, -1):  
            tree[idx] = self._seg_func(tree[2 * idx + 1], tree[2 * idx + 2])

        return tree

    def _generate_indices(self, initial_left: int, initial_right: int) -> Iterator[int]:
        """Generate indices to propagate."""
        left = (initial_left + self._size) >> 1
        right = (initial_right + self._size) >> 1
        lc = 0 if initial_left & 1 else (left & -left).bit_length()
        rc = 0 if initial_right & 1 else (right & -right).bit_length()

        for i in range(self._level):
            if lc <= i:
                yield left
            if rc <= i:
                yield right
            left >>= 1
            right >>= 1

    def _propagate(self, *indices: int) -> None:
        """Propagate a given section."""
        for i in reversed(indices):
            value = self._lazy[i - 1]
            if value is not None:
                self._tree[2 * i - 1] = self._tree[2 * i] = value
                self._lazy[2 * i - 1] = self._lazy[2 * i] = value
                self._lazy[i - 1] = None

    def update(self, left: int, right: int, value: Union[int, str]) -> None:
        """Update [left, right] to value."""
        right += 1  
        if self._modifying_func:
            value = self._modifying_func(value)
        (*indices,) = self._generate_indices(left, right)
        self._propagate(*indices)
        left += self._size
        right += self._size
        while left < right:
            if left & 1:
                self._lazy[left - 1] = self._tree[left - 1] = value
                left += 1
            if right & 1:
                right -= 1
                self._lazy[right - 1] = self._tree[right - 1] = value
            left >>= 1
            right >>= 1

        for i in indices:
            self._tree[i - 1] = self._seg_func(self._tree[2 * i - 1], self._tree[2 * i])

    def query(self, left: int, right: int) -> Union[int, str]:
        """Respond to query in [left, right]."""
        right += 1  
        self._propagate(*self._generate_indices(left, right))
        left += self._size
        right += self._size
        result = self._identity
        while left < right:
            if left & 1:
                result = self._seg_func(result, self._tree[left - 1])
                left += 1
            if right & 1:
                right -= 1
                result = self._seg_func(result, self._tree[right - 1])
            left >>= 1
            right >>= 1
        return result


def main():
    
    import sys

    readline = sys.stdin.buffer.readline
    readlines = sys.stdin.buffer.readlines

    N, _ = map(int, readline().split())
    tree = LazySegmentTree([2 ** 31 - 1] * N, 2 ** 31 - 1, min)
    ans = []
    for query in readlines():
        com, *info = map(int, query.split())
        if com:
            ans.append(tree.query(*info))
        else:
            tree.update(*info)
    print("\n".join(map(str, ans)))


if __name__ == "__main__":
    main()

Solution (Python):

def segfunc(x, y):
    return min(x, y)



ide_ele = 2**31 - 1


class LazySegmentTree:
    """
    init(init_val, ide_ele): 配列init_valで初期化 O(N)
    update(l, r, x): 区間[l, r)をxに更新 O(logN)
    query(l, r): 区間[l, r)をsegfuncしたものを返す O(logN)
    """
    def __init__(self, init_val, segfunc, ide_ele):
        """
        init_val: 配列の初期値
        segfunc: 区間にしたい操作
        ide_ele: 単位元
        num: n以上の最小の2のべき乗
        data: 値配列(1-index)
        lazy: 遅延配列(1-index)
        """
        n = len(init_val)
        self.segfunc = segfunc
        self.ide_ele = ide_ele
        self.num = 1 << (n - 1).bit_length()
        self.data = [ide_ele] * 2 * self.num
        self.lazy = [None] * 2 * self.num
        
        for i in range(n):
            self.data[self.num + i] = init_val[i]
        
        for i in range(self.num - 1, 0, -1):
            self.data[i] = self.segfunc(self.data[2 * i], self.data[2 * i + 1])

    def gindex(self, l, r):
            """
            伝搬する対象の区間を求める
            lm: 伝搬する必要のある最大の左閉区間
            rm: 伝搬する必要のある最大の右開区間
            """
            l += self.num
            r += self.num
            lm = l >> (l & -l).bit_length()
            rm = r >> (r & -r).bit_length()

            while r > l:
                if l <= lm:
                    yield l
                if r <= rm:
                    yield r
                r >>= 1
                l >>= 1
            while l:
                yield l
                l >>= 1

    def propagates(self, *ids):
        """
        遅延伝搬処理
        ids: 伝搬する対象の区間 
        """
        for i in reversed(ids):
            v = self.lazy[i]
            if v is None:
                continue
            self.lazy[2 * i] = v
            self.lazy[2 * i + 1] = v
            self.data[2 * i] = v
            self.data[2 * i + 1] = v
            self.lazy[i] = None

    def update(self, l, r, x):
        """
        区間[l, r)の値をxに更新
        l, r: index(0-index)
        x: update value
        """
        *ids, = self.gindex(l, r)
        self.propagates(*ids)
        l += self.num
        r += self.num
        while l < r:
            if l & 1:
                self.lazy[l] = x
                self.data[l] = x
                l += 1
            if r & 1:
                self.lazy[r - 1] = x
                self.data[r - 1] = x
            r >>= 1
            l >>= 1
        for i in ids:
            self.data[i] = self.segfunc(self.data[2 * i], self.data[2 * i + 1])


    def query(self, l, r):
        """
        [l, r)のsegfuncしたものを得る
        l: index(0-index)
        r: index(0-index)
        """
        *ids, = self.gindex(l, r)
        self.propagates(*ids)

        res = self.ide_ele

        l += self.num
        r += self.num
        while l < r:
            if l & 1:
                res = self.segfunc(res, self.data[l])
                l += 1
            if r & 1:
                res = self.segfunc(res, self.data[r - 1])
            l >>= 1
            r >>= 1
        return res
n, q = [int(x) for x in input().split()]
seg = LazySegmentTree([ide_ele]*n, segfunc, ide_ele)
for _ in range(q):
    a, *b = [int(x) for x in input().split()]
    if a == 1:
        s, t = b
        print(seg.query(s, t + 1))
    else:
        s, t, x = b
        seg.update(s, t + 1, x)

Solution (Python):

class Lazy_SegTree:
    """
    Lazy Segment Tree
        * handle range update
    """

    def __init__(self, n, func, init=float('inf')):
        """__init__
        :param
            n: size of node
            func: merge func
            init: initial value of each nde
        """
        self._n = n
        self.n = 2**(n-1).bit_length()
        self.init = init
        self.data = [init]*(2*self.n)
        self.lazy = [None]*(2*self.n)
        self.func = func

    def set(self, pos, val):
        """set

        :param pos: position of value
        :param val: set value
        """
        self.data[pos+self.n-1] = val

    def build(self):
        """build
            called after set, build initial seg tree
        """
        for k in reversed(range(self.n-1)):
            self.data[k] = self.func(self.data[k*2+1], self.data[k*2+2])

    def _update_range(self, l, r):
        """_update_range
            get range of updated nodes

        :param l:
        :param r:
        """
        L = l+self.n
        R = r+self.n

        lm = (L//(L & -L)) >> 1
        rm = (R//(R & -R)) >> 1
        while L < R:
            if R <= rm:
                yield R
            if L <= lm:
                yield L
            L >>= 1
            R >>= 1
        while L:
            yield L
            L >>= 1

    def _propagates(self, *ids):
        """_propagates
            propagete lazy-value

        :param *ids:
        """
        for i in reversed(ids):
            v = self.lazy[i-1]
            if v is None:
                continue
            self.lazy[2*i-1] = \
                self.data[2*i-1] = self.lazy[2*i] = self.data[2*i] = v
            self.lazy[i-1] = None

    def update(self, l, r, x):
        """update
            update values [l,r) with x
        :param l: left index
        :param r: right index
        :param x: value
        """
        *ids, = self._update_range(l, r)
        self._propagates(*ids)
        L = l+self.n
        R = r + self.n
        while L < R:
            if R & 1:
                R -= 1
                self.lazy[R-1] = self.data[R-1] = x
            if L & 1:
                self.lazy[L-1] = self.data[L-1] = x
                L += 1
            L >>= 1
            R >>= 1
        for i in ids:
            self.data[i-1] = self.func(self.data[2*i-1], self.data[2*i])

    def query(self, l, r):
        """query
            return value [l,r)
        :param l: left index
        :param r: right index
        """
        self._propagates(*self._update_range(l, r))
        L = l + self.n
        R = r + self.n
        ret = self.init

        while L < R:
            if R & 1:
                R -= 1
                ret = self.func(ret, self.data[R-1])
            if L & 1:
                ret = self.func(ret, self.data[L-1])
                L += 1
            L >>= 1
            R >>= 1
        return ret

    def print_tree(self):
        print(" ".join(map(str, [self.data[i+self.n-1]
                                 for i in range(self._n)])))


N, Q = map(int, input().split())
query = [list(map(lambda x:int(x), input().split())) for _ in range(Q)]
Seg = Lazy_SegTree(N+1, min, 2**31-1)
Seg.build()
for q in query:
    if q[0] == 0:
        s = q[1]
        t = q[2]
        x = q[3]
        Seg.update(s, t+1, x)
    else:
        s = q[1]
        t = q[2]
        r = Seg.query(s, t+1)
        print(r)

Solution (Python):
class LazyPropSegmentTree:
    def __init__(self, lst, op, apply, comp, e, identity):
        self.n = len(lst)
        self.depth = (self.n - 1).bit_length()
        self.N = 1 << self.depth
        self.op = op 
        self.apply = apply 
        self.comp = comp 
        self.e = e 
        self.identity = identity 
        self.v = self._build(lst) 
        self.lazy = [self.identity] * (2 * self.N)
    
    def __getitem__(self, i):
        return self.fold(i, i+1)
    
    def _build(self, lst):
        
        
        tree = [self.e] * (self.N) + lst + [self.e] * (self.N - self.n)
        for i in range(self.N - 1, 0, -1): tree[i] = self.op(tree[i << 1], tree[(i << 1)|1])
        return tree
    
    def _indices(self, l, r):
        left = l + self.N; right = r + self.N
        left //= (left & (-left)); right //= (right & (-right))
        left >>= 1; right >>= 1
        while left != right:
            if left > right: yield left; left >>= 1
            else: yield right; right >>= 1
        while left > 0: yield left; left >>= 1
    
    
    def _propagate_topdown(self, *indices):
        identity, v, lazy, apply, comp = self.identity, self.v, self.lazy, self.apply, self.comp
        for k in reversed(indices):
            x = lazy[k]
            if x == identity: continue
            lazy[k << 1] = comp(lazy[k << 1], x)
            lazy[(k << 1)|1] = comp(lazy[(k << 1)|1], x)
            v[k << 1] = apply(v[k << 1], x)
            v[(k << 1)|1] = apply(v[(k << 1)|1], x)
            lazy[k] = identity 

    
    def _propagate_bottomup(self, indices):
        v, op = self.v, self.op
        for k in indices: v[k] = op(v[k << 1], v[(k << 1)|1])

    
    def update(self, l, r, x):
        *indices, = self._indices(l, r)
        self._propagate_topdown(*indices)
        
        N, v, lazy, apply, comp = self.N, self.v, self.lazy, self.apply, self.comp
        
        
        left = l + N; right = r + N
        if left & 1: v[left] = apply(v[left], x); left += 1
        if right & 1: right -= 1; v[right] = apply(v[right], x)
        left >>= 1; right >>= 1
        while left < right:
            if left & 1:
                lazy[left] = comp(lazy[left], x)
                v[left] = apply(v[left], x)
                left += 1
            if right & 1:
                right -= 1
                lazy[right] = comp(lazy[right], x)
                v[right] = apply(v[right], x)
            left >>= 1; right >>= 1
        self._propagate_bottomup(indices)
    
    
    def fold(self, l, r):
        self._propagate_topdown(*self._indices(l, r))
        
        e, N, v, op = self.e, self.N, self.v, self.op
        
        
        left = l + N; right = r + N
        L = R = e
        while left < right:
            if left & 1: 
                L = op(L, v[left])
                left += 1
            if right & 1: 
                right -= 1
                R = op(v[right], R)
            left >>= 1; right >>= 1
        return op(L, R)
    
N, Q = map(int, input().split())
op = min
apply = lambda x, f: f
comp = lambda f, g: g
e = 2**31 - 1
identity = None
A = [e] * N
lpsg = LazyPropSegmentTree(A, op, apply, comp, e, identity)
ans = []
for _ in range(Q):
    t, *arg, = map(int, input().split())
    if t == 0:
        s, t, x = arg
        lpsg.update(s, t+1, x)
    else:
        s, t = arg
        ans.append(lpsg.fold(s, t+1))
print('\n'.join(map(str, ans)))

Solution (Python):




def _gidx(l, r, treesize):
    '''
    lazy propagation用idx生成器 木の下から生成される。1based-indexなので注意.(使うときは-1するとか)
    もとの配列において[l,r)を指定したときに更新すべきidxをyieldする
    treesizeは多くの場合self.num
    '''
    L, R = l + treesize, r + treesize
    lm = (L // (L & -L)) >> 1  
    rm = (R // (R & -R)) >> 1
    while L < R:
        if R <= rm:
            yield R
        if L <= lm:
            yield L
        L >>= 1
        R >>= 1
    while L:  
        yield L
        L >>= 1


class SegmentTreeForRMQ:  
    def __init__(self, ls: list, segfunc=min, identity_element=2**63, lazy_ide=None):
        '''
        セグ木
        一次元のリストlsを受け取り初期化する。O(len(ls))
        区間のルールはsegfuncによって定義される
        identity elementは単位元。e.g., 最小値を求めたい→inf, 和→0, 積→1, gcd→0
        [単位元](https://ja.wikipedia.org/wiki/%E5%8D%98%E4%BD%8D%E5%85%83)
        '''
        self.ide = identity_element
        self.lide = lazy_ide  
        self.func = segfunc
        n = len(ls)
        self.num = 2 ** (n - 1).bit_length()  
        self.tree = [self.ide] * (2 * self.num)  
        self.lazy = [self.lide] * (2 * self.num)  
        for i, l in enumerate(ls):  
            self.tree[i + self.num - 1] = l
        for i in range(self.num - 2, -1, -1):  
            self.tree[i] = segfunc(self.tree[2 * i + 1], self.tree[2 * i + 2])

    def _lazyprop(self, *ids):
        '''
        遅延評価用の関数
        - self.tree[i] に self.lazy[i]の値を伝播させて遅延更新する
        - 子ノードにself.lazyの値を伝播させる **ここは問題ごとに書き換える必要がある**
        - self.lazy[i]をリセットする
        '''
        for i in reversed(ids):
            i -= 1  
            v = self.lazy[i]
            if v == self.lide:
                continue  
            
            
            
            self.tree[2 * i + 1] = v
            self.tree[2 * i + 2] = v
            self.lazy[2 * i + 1] = v
            self.lazy[2 * i + 2] = v
            self.lazy[i] = self.lide  

    def update(self, l, r, x):
        '''
        [l,r)番目の要素をxに変更する(木の中間ノードも更新する) O(logN)
        '''
        
        ids = tuple(_gidx(l, r, self.num))
        self._lazyprop(*ids)
        
        if r <= l:
            return ValueError('invalid index (l,rがありえないよ)')
        l += self.num
        r += self.num
        while l < r:
            if r & 1:
                r -= 1  
                self.tree[r - 1] = x
                self.lazy[r - 1] = x
            if l & 1:
                self.tree[l - 1] = x
                self.tree[l - 1] = x  
                self.lazy[l - 1] = x  
                l += 1
            l >>= 1
            r >>= 1
        
        for i in ids:
            i -= 1  
            self.tree[i] = self.func(
                self.tree[2 * i + 1], self.tree[2 * i + 2])

    def query(self, l, r):
        '''
        区間[l,r)に対するクエリをO(logN)で処理する。例えばその区間の最小値、最大値、gcdなど
        '''
        if r <= l:
            return ValueError('invalid index (l,rがありえないよ)')
        
        self._lazyprop(*_gidx(l, r, self.num))
        
        l += self.num
        r += self.num
        res = self.ide
        while l < r:  
            if r & 1:
                r -= 1
                res = self.func(res, self.tree[r - 1])
            if l & 1:
                res = self.func(res, self.tree[l - 1])
                l += 1
            l >>= 1
            r >>= 1  
        return res


n, q = map(int, input().split())
ls = [2 ** 31 - 1] * n
seg = SegmentTreeForRMQ(ls, identity_element=2**31 - 1)

for _ in range(q):
    cmd, *tmp = map(int, input().split())
    if cmd == 0:
        s, t, x = tmp
        seg.update(s, t + 1, x)
    else:
        s, t = tmp
        print(seg.query(s, t + 1))

Solution (Python):
class LazySegTree: 
    def __init__(self, N):
        self.N = N
        self._N = 1<<((N-1).bit_length())
        self.INF = (1<<31) - 1
        self.node = [self.INF]*2*self._N
        self.lazy = [None]*2*self._N

    def build(self, lis):
        for i in range(self.N):
            self.node[i+self._N-1] = lis[i]
        for i in range(self._N-2,-1,-1):
            self.node[i] = self.node[i*2+1] + self.node[i*2+2]

    def _gindex(self, l, r):
        left = l + self._N; right = r + self._N
        lm = (left // (left & -left)) >> 1
        rm = (right // (right & -right)) >> 1
        while left < right:
            if right <= rm: yield right
            if left <= lm: yield left
            left >>= 1; right >>= 1
        while left:
            yield left
            left >>= 1

    def propagates(self, *ids): 
        for i in reversed(ids):
            v = self.lazy[i-1]
            if v is None: continue
            self.lazy[2*i-1] = self.node[2*i-1] = v
            self.lazy[2*i] = self.node[2*i] = v
            self.lazy[i-1] = None
        return

    def update(self, l, r, x): 
        *ids, = self._gindex(l, r)
        self.propagates(*ids)
        left = l + self._N; right = r + self._N
        while left < right:
            if left & 1:
                self.lazy[left-1] = self.node[left-1] = x
                left += 1
            if right & 1:
                right -= 1
                self.lazy[right-1] = self.node[right-1] = x
            left >>= 1; right >>= 1
        for i in ids:
            self.node[i-1] = min(self.node[2*i-1], self.node[2*i])

    def query(self, l, r):
        self.propagates(*self._gindex(l, r))
        left = l + self._N; right = r + self._N
        ret = self.INF
        while left < right:
            if right & 1:
                right -= 1
                ret = min(ret, self.node[right-1])
            if left & 1:
                ret = min(ret, self.node[left-1])
                left += 1
            left >>= 1; right >>= 1
        return ret

nm = lambda: map(int, input().split())
n,q = nm()
S = LazySegTree(n)
for _ in range(q):
  v = list(nm())
  if v[0]:
    
    
    print(S.query(v[1], v[2]+1))
  else:
  	S.update(v[1], v[2]+1, v[3])

Solution (Python):

def func(x,y):
    return min(x,y)

class LazySegmentTree():
    def __init__(self, n):
        self.lv = (n-1).bit_length()
        self.n = 2 ** self.lv
        self.arr = [2**31-1] * (2*self.n-1)
        self.lazy = [None] * (2*self.n-1)

    
    def getidx(self, l, r):
        L = (l + self.n) >> 1
        R = (r + self.n) >> 1
        lc = 0 if l & 1 else (L & -L).bit_length()
        rc = 0 if r & 1 else (R & -R).bit_length()
        for i in range(self.lv):
            if rc <= i:
                yield R
            if L < R and lc <= i:
                yield L
            L >>= 1; R >>=1

    
    def propagate(self,*ids):
        for i in reversed(ids):
            v = self.lazy[i-1]
            if v is None:
                continue
            self.lazy[2*i-1] = self.arr[2*i-1] = self.lazy[2*i] = self.arr[2*i] = v
            self.lazy[i-1] = None

    
    def update(self,l,r,x):
        
        r+=1
        *ids, = self.getidx(l,r)
        self.propagate(*ids)

        L = self.n + l; R = self.n + r
        while L < R:
            if R & 1:
                R -=1
                self.lazy[R-1] = self.arr[R-1] = x
            if L & 1:
                self.lazy[L-1] = self.arr[L-1] = x
                L += 1
            L >>= 1; R >>= 1

        for i in ids:
            self.arr[i-1] = func(self.arr[2*i-1], self.arr[2*i])

    
    def query(self,l,r):
        
        r+=1
        self.propagate(*self.getidx(l,r))
        L = self.n + l; R = self.n + r

        s = 2**31-1
        while L < R:
            if R & 1:
                R -=1
                s = func(s, self.arr[R-1])
            if L & 1:
                s = func(s, self.arr[L-1])
                L += 1
            L >>= 1; R >>= 1
        return s

import sys
input=sys.stdin.readline
n,q=map(int,input().split())
sg=LazySegmentTree(n)
for _ in range(q):
    que=list(map(int,input().split()))
    if que[0]==0:
        y,l,r,x=que
        sg.update(l,r,x)
    else:
        y,l,r=que
        print(sg.query(l,r))