Buggy python submission (verdict: Time Limit Exceeded):
#!/usr/bin/env python3

import sys

DEBUG = False


class SegmentTree:

    # Use 1-based index internally
    def __init__(self, n, merge_func, fillval=sys.maxsize):
        self.n = 1
        while self.n < n:
            self.n *= 2
        self.nodes = [fillval] * (self.n * 2 - 1 + 1)  # +1 for 1-based index
        self.merge_func = merge_func
        self.fillval = fillval
    
    def update(self, idx, val):
        idx += 1  # for 1-based index

        nodes = self.nodes
        mergef = self.merge_func

        idx += self.n - 1
        nodes[idx] = val
        while idx > 1: # > 1 for 1-based index
            idx >>= 1  # parent(idx) = idx >> 1 for 1-based index segment tree
            nodes[idx] = mergef(nodes[idx << 1], nodes[(idx << 1) + 1]) # child(idx) = idx << 1 and idx << 1 + 1
    
    def query(self, l, r):
        l += 1; r += 1  # for 1-based index

        l += self.n - 1
        r += self.n - 1
        acc = self.fillval
        while l < r:
            if l & 1:
                acc = self.merge_func(acc, self.nodes[l])
                l += 1
            if r & 1:
                acc = self.merge_func(acc, self.nodes[r - 1])
                r -= 1
            l >>= 1
            r >>= 1
        return acc

def read(t = str):
    return t(sys.stdin.readline().rstrip())

def read_list(t = str, sep = " "):
    return [t(s) for s in sys.stdin.readline().rstrip().split(sep)]

def dprint(*args, **kwargs):
    if DEBUG:
        print(*args, **kwargs)
    return


def main():
    read()
    s = read()

    a_ord = ord("a")
    bit_offsets = {chr(c_ord): c_ord - a_ord for c_ord in range(ord("a"), ord("z") + 1)}

    apps = SegmentTree(len(s), lambda x, y: x | y, 0)
    i = 0
    for c in s:
        apps.update(i, 1 << bit_offsets[c])
        i += 1

    nr_q = read(int)
    for i in range(0, nr_q):
        q = read_list()
        if q[0] == "1":
            _, i, c = q  # i_q in the question starts from 1, not 0
            apps.update(int(i) - 1, 1 << bit_offsets[c])
        else:
            _, l, r = q  # l_q and r_q in the question start from 1, not 0
            print(bin(apps.query(int(l) - 1, int(r) - 1 + 1)).count("1"))  # query in the question includes both edges


if __name__ == "__main__":
    main()

Fixed submission:
#!/usr/bin/env python3

import sys

DEBUG = False


class SegmentTree:

    # Use 1-based index internally
    def __init__(self, n, merge_func, fillval=sys.maxsize):
        self.n = 1
        while self.n < n:
            self.n *= 2
        self.nodes = [fillval] * (self.n * 2 - 1 + 1)  # +1 for 1-based index
        self.merge_func = merge_func
        self.fillval = fillval
    
    def update(self, idx, val):
        idx += 1  # for 1-based index

        nodes = self.nodes
        mergef = self.merge_func

        idx += self.n - 1
        nodes[idx] = val
        while idx > 1: # > 1 for 1-based index
            idx >>= 1  # parent(idx) = idx >> 1 for 1-based index segment tree
            nodes[idx] = mergef(nodes[idx << 1], nodes[(idx << 1) + 1]) # child(idx) = idx << 1 and idx << 1 + 1
    
    def query(self, l, r):
        l += 1; r += 1  # for 1-based index

        l += self.n - 1
        r += self.n - 1
        acc = self.fillval
        while l < r:
            if l & 1:
                acc = self.merge_func(acc, self.nodes[l])
                l += 1
            if r & 1:
                acc = self.merge_func(acc, self.nodes[r - 1])
                r -= 1
            l >>= 1
            r >>= 1
        return acc

def read(t = str):
    return t(sys.stdin.readline().rstrip())

def read_list(t = str, sep = " "):
    return [t(s) for s in sys.stdin.readline().rstrip().split(sep)]

def dprint(*args, **kwargs):
    if DEBUG:
        print(*args, **kwargs)
    return


def main():
    read()
    s = read()

    a_ord = ord("a")
    bit_offsets = {chr(c_ord): c_ord - a_ord for c_ord in range(ord("a"), ord("z") + 1)}

    apps = SegmentTree(len(s), lambda x, y: x | y, 0)
    i = 0
    for c in s:
        apps.update(i, 1 << bit_offsets[c])
        i += 1

    nr_q = read(int)
    for i in range(0, nr_q):
        q = read_list()
        if q[0] == "1":
            _, i, c = q  # i_q in the question starts from 1, not 0
            apps.update(int(i) - 1, 1 << bit_offsets[c])
        else:
            _, l, r = q  # l_q and r_q in the question start from 1, not 0
            print(bin(apps.query(int(l) - 1, int(r) - 1 + 1)).count("1"))  # query in the question includes both edges


if __name__ == "__main__":
    main()

Buggy python submission (verdict: Time Limit Exceeded):
#!/usr/bin/env python3

import sys

DEBUG = False

class SegmentTree:

    def __init__(self, n):
        self.n = 1
        while self.n < n:
            self.n *= 2
        self.nodes = [0 for _ in range(self.n * 2)]
    
    def update(self, idx, val):
        nodes = self.nodes

        idx += self.n - 1
        if nodes[idx] == val:
            return

        nodes[idx] = val
        while idx > 0:
            idx = (idx - 1) // 2
            nodes[idx] = nodes[idx * 2 + 1] | nodes[idx * 2 + 2]
    
    def _query(self, l, r, scope_idx, scope_l, scope_r):
        if r <= scope_l or l >= scope_r:
            return 0
        if l <= scope_l and r >= scope_r:
            return self.nodes[scope_idx]
        vl = self._query(l, r, scope_idx * 2 + 1, scope_l, (scope_l + scope_r) // 2)
        vr = self._query(l, r, scope_idx * 2 + 2, (scope_l + scope_r) // 2, scope_r)
        return vl | vr
    
    def query(self, l, r):
        return self._query(l, r, 0, 0, self.n)


def read(t = str):
    return t(sys.stdin.readline().rstrip())

def read_list(t = str, sep = " "):
    return [t(s) for s in sys.stdin.readline().rstrip().split(sep)]

def dprint(*args, **kwargs):
    if DEBUG:
        print(*args, **kwargs)
    return


def main():
    read()
    s = read()

    a_ord = ord("a")
    bit_offsets = {chr(c_ord): c_ord - a_ord for c_ord in range(ord("a"), ord("z") + 1)}

    apps = SegmentTree(len(s))
    i = 0
    for c in s:
        apps.update(i, 1 << bit_offsets[c])
        i += 1

    nr_q = read(int)
    for i in range(0, nr_q):
        q = read_list()
        if q[0] == "1":
            _, i, c = q  # i_q in the question starts from 1, not 0
            apps.update(int(i) - 1, 1 << bit_offsets[c])
        else:
            _, l, r = q  # l_q and r_q in the question start from 1, not 0
            print(bin(apps.query(int(l) - 1, int(r) - 1 + 1)).count("1"))  # query in the question includes both edges


if __name__ == "__main__":
    main()

Fixed submission:
#!/usr/bin/env python3

import sys

DEBUG = False


class SegmentTree:

    # Use 1-based index internally
    def __init__(self, n, merge_func, fillval=sys.maxsize):
        self.n = 1
        while self.n < n:
            self.n *= 2
        self.nodes = [fillval] * (self.n * 2 - 1 + 1)  # +1 for 1-based index
        self.merge_func = merge_func
        self.fillval = fillval
    
    def update(self, idx, val):
        idx += 1  # for 1-based index

        nodes = self.nodes
        mergef = self.merge_func

        idx += self.n - 1
        nodes[idx] = val
        while idx > 1: # > 1 for 1-based index
            idx >>= 1  # parent(idx) = idx >> 1 for 1-based index segment tree
            nodes[idx] = mergef(nodes[idx << 1], nodes[(idx << 1) + 1]) # child(idx) = idx << 1 and idx << 1 + 1
    
    def query(self, l, r):
        l += 1; r += 1  # for 1-based index

        l += self.n - 1
        r += self.n - 1
        acc = self.fillval
        while l < r:
            if l & 1:
                acc = self.merge_func(acc, self.nodes[l])
                l += 1
            if r & 1:
                acc = self.merge_func(acc, self.nodes[r - 1])
                r -= 1
            l >>= 1
            r >>= 1
        return acc

def read(t = str):
    return t(sys.stdin.readline().rstrip())

def read_list(t = str, sep = " "):
    return [t(s) for s in sys.stdin.readline().rstrip().split(sep)]

def dprint(*args, **kwargs):
    if DEBUG:
        print(*args, **kwargs)
    return


def main():
    read()
    s = read()

    a_ord = ord("a")
    bit_offsets = {chr(c_ord): c_ord - a_ord for c_ord in range(ord("a"), ord("z") + 1)}

    apps = SegmentTree(len(s), lambda x, y: x | y, 0)
    i = 0
    for c in s:
        apps.update(i, 1 << bit_offsets[c])
        i += 1

    nr_q = read(int)
    for i in range(0, nr_q):
        q = read_list()
        if q[0] == "1":
            _, i, c = q  # i_q in the question starts from 1, not 0
            apps.update(int(i) - 1, 1 << bit_offsets[c])
        else:
            _, l, r = q  # l_q and r_q in the question start from 1, not 0
            print(bin(apps.query(int(l) - 1, int(r) - 1 + 1)).count("1"))  # query in the question includes both edges


if __name__ == "__main__":
    main()

Buggy python submission (verdict: Time Limit Exceeded):
#!/usr/bin/env python3

import sys

DEBUG = False

class SegmentTree:

    def __init__(self, n):
        self.n = 1
        while self.n < n:
            self.n *= 2
        self.nodes = [0 for _ in range(self.n * 2)]
    
    def update(self, idx, val):
        nodes = self.nodes

        idx += self.n - 1
        nodes[idx] = val
        while idx > 0:
            idx = (idx - 1) // 2
            nodes[idx] = nodes[idx * 2 + 1] | nodes[idx * 2 + 2]
    
    def _query(self, l, r, scope_idx, scope_l, scope_r):
        if r <= scope_l or l >= scope_r:
            return 0
        if l <= scope_l and r >= scope_r:
            return self.nodes[scope_idx]
        vl = self._query(l, r, scope_idx * 2 + 1, scope_l, (scope_l + scope_r) // 2)
        vr = self._query(l, r, scope_idx * 2 + 2, (scope_l + scope_r) // 2, scope_r)
        return vl | vr
    
    def query(self, l, r):
        return self._query(l, r, 0, 0, self.n)


def read(t = str):
    return t(sys.stdin.readline().rstrip())

def read_list(t = str, sep = " "):
    return [t(s) for s in sys.stdin.readline().rstrip().split(sep)]

def dprint(*args, **kwargs):
    if DEBUG:
        print(*args, **kwargs)
    return


def main():
    read()
    s = read()

    a_ord = ord("a")
    bit_offsets = {chr(c_ord): c_ord - a_ord for c_ord in range(ord("a"), ord("z") + 1)}

    apps = SegmentTree(len(s))
    i = 0
    for c in s:
        apps.update(i, 1 << bit_offsets[c])
        i += 1

    nr_q = read(int)
    for i in range(0, nr_q):
        q = read_list()
        if q[0] == "1":
            _, i, c = q  # i_q in the question starts from 1, not 0
            apps.update(int(i) - 1, 1 << bit_offsets[c])
        else:
            _, l, r = q  # l_q and r_q in the question start from 1, not 0
            print(bin(apps.query(int(l) - 1, int(r) - 1 + 1)).count("1"))  # query in the question includes both edges


if __name__ == "__main__":
    main()

Fixed submission:
#!/usr/bin/env python3

import sys

DEBUG = False


class SegmentTree:

    # Use 1-based index internally
    def __init__(self, n, merge_func, fillval=sys.maxsize):
        self.n = 1
        while self.n < n:
            self.n *= 2
        self.nodes = [fillval] * (self.n * 2 - 1 + 1)  # +1 for 1-based index
        self.merge_func = merge_func
        self.fillval = fillval
    
    def update(self, idx, val):
        idx += 1  # for 1-based index

        nodes = self.nodes
        mergef = self.merge_func

        idx += self.n - 1
        nodes[idx] = val
        while idx > 1: # > 1 for 1-based index
            idx >>= 1  # parent(idx) = idx >> 1 for 1-based index segment tree
            nodes[idx] = mergef(nodes[idx << 1], nodes[(idx << 1) + 1]) # child(idx) = idx << 1 and idx << 1 + 1
    
    def query(self, l, r):
        l += 1; r += 1  # for 1-based index

        l += self.n - 1
        r += self.n - 1
        acc = self.fillval
        while l < r:
            if l & 1:
                acc = self.merge_func(acc, self.nodes[l])
                l += 1
            if r & 1:
                acc = self.merge_func(acc, self.nodes[r - 1])
                r -= 1
            l >>= 1
            r >>= 1
        return acc

def read(t = str):
    return t(sys.stdin.readline().rstrip())

def read_list(t = str, sep = " "):
    return [t(s) for s in sys.stdin.readline().rstrip().split(sep)]

def dprint(*args, **kwargs):
    if DEBUG:
        print(*args, **kwargs)
    return


def main():
    read()
    s = read()

    a_ord = ord("a")
    bit_offsets = {chr(c_ord): c_ord - a_ord for c_ord in range(ord("a"), ord("z") + 1)}

    apps = SegmentTree(len(s), lambda x, y: x | y, 0)
    i = 0
    for c in s:
        apps.update(i, 1 << bit_offsets[c])
        i += 1

    nr_q = read(int)
    for i in range(0, nr_q):
        q = read_list()
        if q[0] == "1":
            _, i, c = q  # i_q in the question starts from 1, not 0
            apps.update(int(i) - 1, 1 << bit_offsets[c])
        else:
            _, l, r = q  # l_q and r_q in the question start from 1, not 0
            print(bin(apps.query(int(l) - 1, int(r) - 1 + 1)).count("1"))  # query in the question includes both edges


if __name__ == "__main__":
    main()

Buggy python submission (verdict: Runtime Error):
#!/usr/bin/env python3

import sys

DEBUG = False

class SegmentTree:

    def __init__(self, n):
        self.n = 1
        while self.n < n:
            self.n *= 2
        self.nodes = [0 for _ in range(self.n * 2)]
    
    def update(self, idx, val):
        nodes = self.nodes

        idx += self.n - 1
        nodes[idx] = val
        while idx > 0:
            idx = (idx - 1) // 2
            nodes[idx] = nodes[idx * 2 + 1] | nodes[idx * 2 + 2]
    
    def _query(self, l, r, scope_idx, scope_l, scope_r):
        if r <= scope_l or l >= scope_r:
            return 0
        if l <= scope_l and r >= scope_r:
            return self.nodes[scope_idx]
        vl = self._query(l, r, scope_idx * 2 + 1, scope_l, (scope_l + scope_r) // 2)
        vr = self._query(l, r, scope_idx * 2 + 2, (scope_l + scope_r) // 2, scope_r)
        return vl | vr
    
    def query(self, l, r):
        return self._query(l, r, 0, 0, self.n)


def read(t = str):
    return t(sys.stdin.readline().rstrip())

def read_list(t = str, sep = " "):
    return [t(s) for s in sys.stdin.readline().rstrip().split(sep)]

def dprint(*args, **kwargs):
    if DEBUG:
        print(*args, **kwargs)
    return


def main():
    read()
    s = read()

    a_ord = ord("a")
    bit_offsets = {chr(c_ord): c_ord - a_ord for c_ord in range(ord("a"), ord("z"))}

    apps = SegmentTree(len(s))
    i = 0
    for c in s:
        apps.update(i, 1 << bit_offsets[c])
        i += 1

    nr_q = read(int)
    for i in range(0, nr_q):
        q = read_list()
        if q[0] == "1":
            _, i, c = q  # i_q in the question starts from 1, not 0
            apps.update(int(i) - 1, 1 << bit_offsets[c])
        else:
            _, l, r = q  # l_q and r_q in the question start from 1, not 0
            print(bin(apps.query(int(l) - 1, int(r) - 1 + 1)).count("1"))  # query in the question includes both edges


if __name__ == "__main__":
    main()

Fixed submission:
#!/usr/bin/env python3

import sys

DEBUG = False


class SegmentTree:

    # Use 1-based index internally
    def __init__(self, n, merge_func, fillval=sys.maxsize):
        self.n = 1
        while self.n < n:
            self.n *= 2
        self.nodes = [fillval] * (self.n * 2 - 1 + 1)  # +1 for 1-based index
        self.merge_func = merge_func
        self.fillval = fillval
    
    def update(self, idx, val):
        idx += 1  # for 1-based index

        nodes = self.nodes
        mergef = self.merge_func

        idx += self.n - 1
        nodes[idx] = val
        while idx > 1: # > 1 for 1-based index
            idx >>= 1  # parent(idx) = idx >> 1 for 1-based index segment tree
            nodes[idx] = mergef(nodes[idx << 1], nodes[(idx << 1) + 1]) # child(idx) = idx << 1 and idx << 1 + 1
    
    def query(self, l, r):
        l += 1; r += 1  # for 1-based index

        l += self.n - 1
        r += self.n - 1
        acc = self.fillval
        while l < r:
            if l & 1:
                acc = self.merge_func(acc, self.nodes[l])
                l += 1
            if r & 1:
                acc = self.merge_func(acc, self.nodes[r - 1])
                r -= 1
            l >>= 1
            r >>= 1
        return acc

def read(t = str):
    return t(sys.stdin.readline().rstrip())

def read_list(t = str, sep = " "):
    return [t(s) for s in sys.stdin.readline().rstrip().split(sep)]

def dprint(*args, **kwargs):
    if DEBUG:
        print(*args, **kwargs)
    return


def main():
    read()
    s = read()

    a_ord = ord("a")
    bit_offsets = {chr(c_ord): c_ord - a_ord for c_ord in range(ord("a"), ord("z") + 1)}

    apps = SegmentTree(len(s), lambda x, y: x | y, 0)
    i = 0
    for c in s:
        apps.update(i, 1 << bit_offsets[c])
        i += 1

    nr_q = read(int)
    for i in range(0, nr_q):
        q = read_list()
        if q[0] == "1":
            _, i, c = q  # i_q in the question starts from 1, not 0
            apps.update(int(i) - 1, 1 << bit_offsets[c])
        else:
            _, l, r = q  # l_q and r_q in the question start from 1, not 0
            print(bin(apps.query(int(l) - 1, int(r) - 1 + 1)).count("1"))  # query in the question includes both edges


if __name__ == "__main__":
    main()

Buggy python submission (verdict: Runtime Error):
#!/usr/bin/env python3

import sys

DEBUG = False

class SegmentTree:

    def __init__(self, n):
        self.n = 1
        while self.n < n:
            self.n *= 2
        self.nodes = [0 for _ in range(self.n * 2)]
    
    def update(self, idx, val):
        nodes = self.nodes

        idx += self.n - 1
        nodes[idx] = val
        while idx > 0:
            idx = (idx - 1) // 2
            nodes[idx] = nodes[idx * 2 + 1] | nodes[idx * 2 + 2]
    
    def _query(self, l, r, scope_idx, scope_l, scope_r):
        if r <= scope_l or l >= scope_r:
            return 0
        if l <= scope_l and r >= scope_r:
            return self.nodes[scope_idx]
        vl = self._query(l, r, scope_idx * 2 + 1, scope_l, (scope_l + scope_r) // 2)
        vr = self._query(l, r, scope_idx * 2 + 2, (scope_l + scope_r) // 2, scope_r)
        return vl | vr
    
    def query(self, l, r):
        return self._query(l, r, 0, 0, self.n)


def read(t = str):
    return t(sys.stdin.readline().rstrip())

def read_list(t = str, sep = " "):
    return [t(s) for s in sys.stdin.readline().rstrip().split(sep)]

def dprint(*args, **kwargs):
    if DEBUG:
        print(*args, **kwargs)
    return


def main():
    read()
    s = read()

    bit_offsets = {chr(c_ord): ord(c_ord) - ord("a") for c_ord in range(ord("a"), ord("z"))}

    apps = SegmentTree(len(s))
    i = 0
    for c in s:
        apps.update(i, 1 << bit_offsets[c])
        i += 1

    nr_q = read(int)
    for i in range(0, nr_q):
        q = read_list()
        if q[0] == "1":
            _, i, c = q  # i_q in the question starts from 1, not 0
            apps.update(int(i) - 1, 1 << bit_offsets[c])
        else:
            _, l, r = q  # l_q and r_q in the question start from 1, not 0
            print(bin(apps.query(int(l) - 1, int(r) - 1 + 1)).count("1"))  # query in the question includes both edges


if __name__ == "__main__":
    main()

Fixed submission:
#!/usr/bin/env python3

import sys

DEBUG = False


class SegmentTree:

    # Use 1-based index internally
    def __init__(self, n, merge_func, fillval=sys.maxsize):
        self.n = 1
        while self.n < n:
            self.n *= 2
        self.nodes = [fillval] * (self.n * 2 - 1 + 1)  # +1 for 1-based index
        self.merge_func = merge_func
        self.fillval = fillval
    
    def update(self, idx, val):
        idx += 1  # for 1-based index

        nodes = self.nodes
        mergef = self.merge_func

        idx += self.n - 1
        nodes[idx] = val
        while idx > 1: # > 1 for 1-based index
            idx >>= 1  # parent(idx) = idx >> 1 for 1-based index segment tree
            nodes[idx] = mergef(nodes[idx << 1], nodes[(idx << 1) + 1]) # child(idx) = idx << 1 and idx << 1 + 1
    
    def query(self, l, r):
        l += 1; r += 1  # for 1-based index

        l += self.n - 1
        r += self.n - 1
        acc = self.fillval
        while l < r:
            if l & 1:
                acc = self.merge_func(acc, self.nodes[l])
                l += 1
            if r & 1:
                acc = self.merge_func(acc, self.nodes[r - 1])
                r -= 1
            l >>= 1
            r >>= 1
        return acc

def read(t = str):
    return t(sys.stdin.readline().rstrip())

def read_list(t = str, sep = " "):
    return [t(s) for s in sys.stdin.readline().rstrip().split(sep)]

def dprint(*args, **kwargs):
    if DEBUG:
        print(*args, **kwargs)
    return


def main():
    read()
    s = read()

    a_ord = ord("a")
    bit_offsets = {chr(c_ord): c_ord - a_ord for c_ord in range(ord("a"), ord("z") + 1)}

    apps = SegmentTree(len(s), lambda x, y: x | y, 0)
    i = 0
    for c in s:
        apps.update(i, 1 << bit_offsets[c])
        i += 1

    nr_q = read(int)
    for i in range(0, nr_q):
        q = read_list()
        if q[0] == "1":
            _, i, c = q  # i_q in the question starts from 1, not 0
            apps.update(int(i) - 1, 1 << bit_offsets[c])
        else:
            _, l, r = q  # l_q and r_q in the question start from 1, not 0
            print(bin(apps.query(int(l) - 1, int(r) - 1 + 1)).count("1"))  # query in the question includes both edges


if __name__ == "__main__":
    main()

Buggy python submission (verdict: Time Limit Exceeded):
#!/usr/bin/env python3

import sys

DEBUG = False

class SegmentTree:

    def __init__(self, n):
        self.n = 1
        while self.n < n:
            self.n *= 2
        self.fillval = 0
        self.nodes = [0] * (2 * self.n)
    
    def update(self, idx, val):
        nodes = self.nodes

        idx += self.n - 1
        nodes[idx] = val
        while idx > 0:
            idx = (idx - 1) // 2
            nodes[idx] = nodes[idx * 2 + 1] | nodes[idx * 2 + 2]
    
    def _query(self, l, r, scope_idx, scope_l, scope_r):
        if r <= scope_l or l >= scope_r:
            return 0
        if l <= scope_l and r >= scope_r:
            return self.nodes[scope_idx]
        vl = self._query(l, r, scope_idx * 2 + 1, scope_l, (scope_l + scope_r) // 2)
        vr = self._query(l, r, scope_idx * 2 + 2, (scope_l + scope_r) // 2, scope_r)
        return vl | vr
    
    def query(self, l, r):
        return self._query(l, r, 0, 0, self.n)


def read(t = str):
    return t(sys.stdin.readline().rstrip())

def read_list(t = str, sep = " "):
    return [t(s) for s in sys.stdin.readline().rstrip().split(sep)]

def dprint(*args, **kwargs):
    if DEBUG:
        print(*args, **kwargs)
    return


def main():
    read()
    s = read()

    apps = SegmentTree(len(s))
    i = 0
    for c in s:
        bit = ord(c) - ord("a")
        apps.update(i, 1 << bit)
        i += 1

    nr_q = read(int)
    for i in range(0, nr_q):
        q = read_list()
        if q[0] == "1":
            _, i, c = q  # i_q in the question starts from 1, not 0
            bit = ord(c) - ord("a")
            apps.update(int(i) - 1, 1 << bit)
        else:
            _, l, r = q  # l_q and r_q in the question start from 1, not 0
            print(bin(apps.query(int(l) - 1, int(r) - 1 + 1)).count("1"))  # query in the question includes both edges


if __name__ == "__main__":
    main()

Fixed submission:
#!/usr/bin/env python3

import sys

DEBUG = False


class SegmentTree:

    # Use 1-based index internally
    def __init__(self, n, merge_func, fillval=sys.maxsize):
        self.n = 1
        while self.n < n:
            self.n *= 2
        self.nodes = [fillval] * (self.n * 2 - 1 + 1)  # +1 for 1-based index
        self.merge_func = merge_func
        self.fillval = fillval
    
    def update(self, idx, val):
        idx += 1  # for 1-based index

        nodes = self.nodes
        mergef = self.merge_func

        idx += self.n - 1
        nodes[idx] = val
        while idx > 1: # > 1 for 1-based index
            idx >>= 1  # parent(idx) = idx >> 1 for 1-based index segment tree
            nodes[idx] = mergef(nodes[idx << 1], nodes[(idx << 1) + 1]) # child(idx) = idx << 1 and idx << 1 + 1
    
    def query(self, l, r):
        l += 1; r += 1  # for 1-based index

        l += self.n - 1
        r += self.n - 1
        acc = self.fillval
        while l < r:
            if l & 1:
                acc = self.merge_func(acc, self.nodes[l])
                l += 1
            if r & 1:
                acc = self.merge_func(acc, self.nodes[r - 1])
                r -= 1
            l >>= 1
            r >>= 1
        return acc

def read(t = str):
    return t(sys.stdin.readline().rstrip())

def read_list(t = str, sep = " "):
    return [t(s) for s in sys.stdin.readline().rstrip().split(sep)]

def dprint(*args, **kwargs):
    if DEBUG:
        print(*args, **kwargs)
    return


def main():
    read()
    s = read()

    a_ord = ord("a")
    bit_offsets = {chr(c_ord): c_ord - a_ord for c_ord in range(ord("a"), ord("z") + 1)}

    apps = SegmentTree(len(s), lambda x, y: x | y, 0)
    i = 0
    for c in s:
        apps.update(i, 1 << bit_offsets[c])
        i += 1

    nr_q = read(int)
    for i in range(0, nr_q):
        q = read_list()
        if q[0] == "1":
            _, i, c = q  # i_q in the question starts from 1, not 0
            apps.update(int(i) - 1, 1 << bit_offsets[c])
        else:
            _, l, r = q  # l_q and r_q in the question start from 1, not 0
            print(bin(apps.query(int(l) - 1, int(r) - 1 + 1)).count("1"))  # query in the question includes both edges


if __name__ == "__main__":
    main()

Buggy python submission (verdict: Time Limit Exceeded):
#!/usr/bin/env python3

import sys

DEBUG = False

class SegmentTree:

    def __init__(self, n):
        self.n = 1
        while self.n < n:
            self.n *= 2
        self.fillval = 0
        self.nodes = [0] * (2 * self.n)
    
    def update(self, idx, val):
        nodes = self.nodes

        idx += self.n - 1
        nodes[idx] = val
        while idx > 0:
            idx = (idx - 1) // 2
            nodes[idx] = nodes[idx * 2 + 1] | nodes[idx * 2 + 2]
    
    
    def query(self, l, r):
        fillval = self.fillval
        nodes = self.nodes
        
        def _query(l, r, scope_idx, scope_l, scope_r):
            if r <= scope_l or l >= scope_r:
                return fillval
            if l <= scope_l and r >= scope_r:
                return nodes[scope_idx]
            vl = _query(l, r, scope_idx * 2 + 1, scope_l, (scope_l + scope_r) // 2)
            vr = _query(l, r, scope_idx * 2 + 2, (scope_l + scope_r) // 2, scope_r)
            return vl | vr

        return _query(l, r, 0, 0, self.n)


def read(t = str):
    return t(sys.stdin.readline().rstrip())

def read_list(t = str, sep = " "):
    return [t(s) for s in sys.stdin.readline().rstrip().split(sep)]

def dprint(*args, **kwargs):
    if DEBUG:
        print(*args, **kwargs)
    return


def main():
    read()
    s = read()

    apps = SegmentTree(len(s))
    i = 0
    for c in s:
        bit = ord(c) - ord("a")
        apps.update(i, 1 << bit)
        i += 1

    nr_q = read(int)
    for i in range(0, nr_q):
        q = read_list()
        if q[0] == "1":
            _, i, c = q  # i_q in the question starts from 1, not 0
            bit = ord(c) - ord("a")
            apps.update(int(i) - 1, 1 << bit)
        else:
            _, l, r = q  # l_q and r_q in the question start from 1, not 0
            print(bin(apps.query(int(l) - 1, int(r) - 1 + 1)).count("1"))  # query in the question includes both edges


if __name__ == "__main__":
    main()

Fixed submission:
#!/usr/bin/env python3

import sys

DEBUG = False


class SegmentTree:

    # Use 1-based index internally
    def __init__(self, n, merge_func, fillval=sys.maxsize):
        self.n = 1
        while self.n < n:
            self.n *= 2
        self.nodes = [fillval] * (self.n * 2 - 1 + 1)  # +1 for 1-based index
        self.merge_func = merge_func
        self.fillval = fillval
    
    def update(self, idx, val):
        idx += 1  # for 1-based index

        nodes = self.nodes
        mergef = self.merge_func

        idx += self.n - 1
        nodes[idx] = val
        while idx > 1: # > 1 for 1-based index
            idx >>= 1  # parent(idx) = idx >> 1 for 1-based index segment tree
            nodes[idx] = mergef(nodes[idx << 1], nodes[(idx << 1) + 1]) # child(idx) = idx << 1 and idx << 1 + 1
    
    def query(self, l, r):
        l += 1; r += 1  # for 1-based index

        l += self.n - 1
        r += self.n - 1
        acc = self.fillval
        while l < r:
            if l & 1:
                acc = self.merge_func(acc, self.nodes[l])
                l += 1
            if r & 1:
                acc = self.merge_func(acc, self.nodes[r - 1])
                r -= 1
            l >>= 1
            r >>= 1
        return acc

def read(t = str):
    return t(sys.stdin.readline().rstrip())

def read_list(t = str, sep = " "):
    return [t(s) for s in sys.stdin.readline().rstrip().split(sep)]

def dprint(*args, **kwargs):
    if DEBUG:
        print(*args, **kwargs)
    return


def main():
    read()
    s = read()

    a_ord = ord("a")
    bit_offsets = {chr(c_ord): c_ord - a_ord for c_ord in range(ord("a"), ord("z") + 1)}

    apps = SegmentTree(len(s), lambda x, y: x | y, 0)
    i = 0
    for c in s:
        apps.update(i, 1 << bit_offsets[c])
        i += 1

    nr_q = read(int)
    for i in range(0, nr_q):
        q = read_list()
        if q[0] == "1":
            _, i, c = q  # i_q in the question starts from 1, not 0
            apps.update(int(i) - 1, 1 << bit_offsets[c])
        else:
            _, l, r = q  # l_q and r_q in the question start from 1, not 0
            print(bin(apps.query(int(l) - 1, int(r) - 1 + 1)).count("1"))  # query in the question includes both edges


if __name__ == "__main__":
    main()

Buggy python submission (verdict: Runtime Error):
#!/usr/bin/env python3

import sys

DEBUG = False

class SegmentTree:

    def __init__(self, n):
        self.n = 1
        while self.n < n:
            self.n *= 2
        self.fillval = 0
        self.nodes = [0 for _ in range(self.n)]
    
    def update(self, idx, val):
        nodes = self.nodes

        idx += self.n - 1
        nodes[idx] = val
        while idx > 0:
            idx = (idx - 1) // 2
            nodes[idx] = nodes[idx * 2 + 1] | nodes[idx * 2 + 2]
    
    
    def query(self, l, r):
        fillval = self.fillval
        nodes = self.nodes
        
        def _query(l, r, scope_idx, scope_l, scope_r):
            if r <= scope_l or l >= scope_r:
                return fillval
            if l <= scope_l and r >= scope_r:
                return nodes[scope_idx]
            vl = _query(l, r, scope_idx * 2 + 1, scope_l, (scope_l + scope_r) // 2)
            vr = _query(l, r, scope_idx * 2 + 2, (scope_l + scope_r) // 2, scope_r)
            return vl | vr

        return _query(l, r, 0, 0, self.n)


def read(t = str):
    return t(sys.stdin.readline().rstrip())

def read_list(t = str, sep = " "):
    return [t(s) for s in sys.stdin.readline().rstrip().split(sep)]

def dprint(*args, **kwargs):
    if DEBUG:
        print(*args, **kwargs)
    return

class LetterAppearance:
    def __init__(self, chars):
        self.chars_app = SegmentTree(len(chars))
        for i in range(0, len(chars)):
            bit = ord(chars[i]) - ord("a")
            self.chars_app.update(i, 1 << bit)
    
    def replace(self, pos, char):
        bit = ord(char) - ord("a")
        self.chars_app.update(pos, 1 << bit)

    def query(self, l, r):
        return bin(self.chars_app.query(l, r)).count("1")


def main():
    read()
    s = read()
    apps = LetterAppearance(s)
    nr_q = read(int)
    for i in range(0, nr_q):
        q = read_list()
        if q[0] == "1":
            _, i, c = q  # i_q in the question starts from 1, not 0
            apps.replace(int(i) - 1, c)
        else:
            _, l, r = q  # l_q and r_q in the question start from 1, not 0
            print(apps.query(int(l) - 1, int(r) - 1 + 1))  # query in the question includes both edges


if __name__ == "__main__":
    main()

Fixed submission:
#!/usr/bin/env python3

import sys

DEBUG = False


class SegmentTree:

    # Use 1-based index internally
    def __init__(self, n, merge_func, fillval=sys.maxsize):
        self.n = 1
        while self.n < n:
            self.n *= 2
        self.nodes = [fillval] * (self.n * 2 - 1 + 1)  # +1 for 1-based index
        self.merge_func = merge_func
        self.fillval = fillval
    
    def update(self, idx, val):
        idx += 1  # for 1-based index

        nodes = self.nodes
        mergef = self.merge_func

        idx += self.n - 1
        nodes[idx] = val
        while idx > 1: # > 1 for 1-based index
            idx >>= 1  # parent(idx) = idx >> 1 for 1-based index segment tree
            nodes[idx] = mergef(nodes[idx << 1], nodes[(idx << 1) + 1]) # child(idx) = idx << 1 and idx << 1 + 1
    
    def query(self, l, r):
        l += 1; r += 1  # for 1-based index

        l += self.n - 1
        r += self.n - 1
        acc = self.fillval
        while l < r:
            if l & 1:
                acc = self.merge_func(acc, self.nodes[l])
                l += 1
            if r & 1:
                acc = self.merge_func(acc, self.nodes[r - 1])
                r -= 1
            l >>= 1
            r >>= 1
        return acc

def read(t = str):
    return t(sys.stdin.readline().rstrip())

def read_list(t = str, sep = " "):
    return [t(s) for s in sys.stdin.readline().rstrip().split(sep)]

def dprint(*args, **kwargs):
    if DEBUG:
        print(*args, **kwargs)
    return


def main():
    read()
    s = read()

    a_ord = ord("a")
    bit_offsets = {chr(c_ord): c_ord - a_ord for c_ord in range(ord("a"), ord("z") + 1)}

    apps = SegmentTree(len(s), lambda x, y: x | y, 0)
    i = 0
    for c in s:
        apps.update(i, 1 << bit_offsets[c])
        i += 1

    nr_q = read(int)
    for i in range(0, nr_q):
        q = read_list()
        if q[0] == "1":
            _, i, c = q  # i_q in the question starts from 1, not 0
            apps.update(int(i) - 1, 1 << bit_offsets[c])
        else:
            _, l, r = q  # l_q and r_q in the question start from 1, not 0
            print(bin(apps.query(int(l) - 1, int(r) - 1 + 1)).count("1"))  # query in the question includes both edges


if __name__ == "__main__":
    main()

Buggy python submission (verdict: Time Limit Exceeded):
#!/usr/bin/env python3

import sys

DEBUG = False

class SegmentTree:

    def __init__(self, n):
        self.n = 1
        while self.n < n:
            self.n *= 2
        self.fillval = 0
        self.nodes = [0 for _ in range(2 * self.n)]
    
    def update(self, idx, val):
        nodes = self.nodes

        idx += self.n - 1
        nodes[idx] = val
        while idx > 0:
            idx = (idx - 1) // 2
            nodes[idx] = nodes[idx * 2 + 1] | nodes[idx * 2 + 2]
    
    
    def query(self, l, r):
        fillval = self.fillval
        nodes = self.nodes
        
        def _query(l, r, scope_idx, scope_l, scope_r):
            if r <= scope_l or l >= scope_r:
                return fillval
            if l <= scope_l and r >= scope_r:
                return nodes[scope_idx]
            vl = _query(l, r, scope_idx * 2 + 1, scope_l, (scope_l + scope_r) // 2)
            vr = _query(l, r, scope_idx * 2 + 2, (scope_l + scope_r) // 2, scope_r)
            return vl | vr

        return _query(l, r, 0, 0, self.n)


def read(t = str):
    return t(sys.stdin.readline().rstrip())

def read_list(t = str, sep = " "):
    return [t(s) for s in sys.stdin.readline().rstrip().split(sep)]

def dprint(*args, **kwargs):
    if DEBUG:
        print(*args, **kwargs)
    return

class LetterAppearance:
    def __init__(self, chars):
        self.chars_app = SegmentTree(len(chars))
        for i in range(0, len(chars)):
            bit = ord(chars[i]) - ord("a")
            self.chars_app.update(i, 1 << bit)
    
    def replace(self, pos, char):
        bit = ord(char) - ord("a")
        self.chars_app.update(pos, 1 << bit)

    def query(self, l, r):
        return bin(self.chars_app.query(l, r)).count("1")


def main():
    read()
    s = read()
    apps = LetterAppearance(s)
    nr_q = read(int)
    for i in range(0, nr_q):
        q = read_list()
        if q[0] == "1":
            _, i, c = q  # i_q in the question starts from 1, not 0
            apps.replace(int(i) - 1, c)
        else:
            _, l, r = q  # l_q and r_q in the question start from 1, not 0
            print(apps.query(int(l) - 1, int(r) - 1 + 1))  # query in the question includes both edges


if __name__ == "__main__":
    main()

Fixed submission:
#!/usr/bin/env python3

import sys

DEBUG = False


class SegmentTree:

    # Use 1-based index internally
    def __init__(self, n, merge_func, fillval=sys.maxsize):
        self.n = 1
        while self.n < n:
            self.n *= 2
        self.nodes = [fillval] * (self.n * 2 - 1 + 1)  # +1 for 1-based index
        self.merge_func = merge_func
        self.fillval = fillval
    
    def update(self, idx, val):
        idx += 1  # for 1-based index

        nodes = self.nodes
        mergef = self.merge_func

        idx += self.n - 1
        nodes[idx] = val
        while idx > 1: # > 1 for 1-based index
            idx >>= 1  # parent(idx) = idx >> 1 for 1-based index segment tree
            nodes[idx] = mergef(nodes[idx << 1], nodes[(idx << 1) + 1]) # child(idx) = idx << 1 and idx << 1 + 1
    
    def query(self, l, r):
        l += 1; r += 1  # for 1-based index

        l += self.n - 1
        r += self.n - 1
        acc = self.fillval
        while l < r:
            if l & 1:
                acc = self.merge_func(acc, self.nodes[l])
                l += 1
            if r & 1:
                acc = self.merge_func(acc, self.nodes[r - 1])
                r -= 1
            l >>= 1
            r >>= 1
        return acc

def read(t = str):
    return t(sys.stdin.readline().rstrip())

def read_list(t = str, sep = " "):
    return [t(s) for s in sys.stdin.readline().rstrip().split(sep)]

def dprint(*args, **kwargs):
    if DEBUG:
        print(*args, **kwargs)
    return


def main():
    read()
    s = read()

    a_ord = ord("a")
    bit_offsets = {chr(c_ord): c_ord - a_ord for c_ord in range(ord("a"), ord("z") + 1)}

    apps = SegmentTree(len(s), lambda x, y: x | y, 0)
    i = 0
    for c in s:
        apps.update(i, 1 << bit_offsets[c])
        i += 1

    nr_q = read(int)
    for i in range(0, nr_q):
        q = read_list()
        if q[0] == "1":
            _, i, c = q  # i_q in the question starts from 1, not 0
            apps.update(int(i) - 1, 1 << bit_offsets[c])
        else:
            _, l, r = q  # l_q and r_q in the question start from 1, not 0
            print(bin(apps.query(int(l) - 1, int(r) - 1 + 1)).count("1"))  # query in the question includes both edges


if __name__ == "__main__":
    main()

Buggy python submission (verdict: Time Limit Exceeded):
#!/usr/bin/env python3

import sys

DEBUG = False

class SegmentTree:

    def __init__(self, n):
        self.n = 1
        while self.n < n:
            self.n *= 2
        self.fillval = set()
        self.nodes = [set() for _ in range(2 * self.n)]
    
    def update(self, idx, val):
        nodes = self.nodes

        idx += self.n - 1
        nodes[idx] = val
        while idx > 0:
            idx = (idx - 1) // 2
            nodes[idx] = nodes[idx * 2 + 1] | nodes[idx * 2 + 2]
    
    
    def query(self, l, r):
        fillval = self.fillval
        nodes = self.nodes
        
        def _query(l, r, scope_idx, scope_l, scope_r):
            if r <= scope_l or l >= scope_r:
                return fillval
            if l <= scope_l and r >= scope_r:
                return nodes[scope_idx]
            vl = _query(l, r, scope_idx * 2 + 1, scope_l, (scope_l + scope_r) // 2)
            vr = _query(l, r, scope_idx * 2 + 2, (scope_l + scope_r) // 2, scope_r)
            return vl | vr

        return _query(l, r, 0, 0, self.n)


def read(t = str):
    return t(sys.stdin.readline().rstrip())

def read_list(t = str, sep = " "):
    return [t(s) for s in sys.stdin.readline().rstrip().split(sep)]

def dprint(*args, **kwargs):
    if DEBUG:
        print(*args, **kwargs)
    return

class LetterAppearance:
    def __init__(self, chars):
        self.chars_app = SegmentTree(len(chars))
        for i in range(0, len(chars)):
            self.chars_app.update(i, set([chars[i]]))
    
    def replace(self, pos, char):
        self.chars_app.update(pos, set([char]))

    def query(self, l, r):
        return len(self.chars_app.query(l, r))


def main():
    read()
    s = read()
    apps = LetterAppearance(s)
    nr_q = read(int)
    for i in range(0, nr_q):
        q = read_list()
        if q[0] == "1":
            _, i, c = q  # i_q in the question starts from 1, not 0
            apps.replace(int(i) - 1, c)
        else:
            _, l, r = q  # l_q and r_q in the question start from 1, not 0
            print(apps.query(int(l) - 1, int(r) - 1 + 1))  # query in the question includes both edges


if __name__ == "__main__":
    main()

Fixed submission:
#!/usr/bin/env python3

import sys

DEBUG = False


class SegmentTree:

    # Use 1-based index internally
    def __init__(self, n, merge_func, fillval=sys.maxsize):
        self.n = 1
        while self.n < n:
            self.n *= 2
        self.nodes = [fillval] * (self.n * 2 - 1 + 1)  # +1 for 1-based index
        self.merge_func = merge_func
        self.fillval = fillval
    
    def update(self, idx, val):
        idx += 1  # for 1-based index

        nodes = self.nodes
        mergef = self.merge_func

        idx += self.n - 1
        nodes[idx] = val
        while idx > 1: # > 1 for 1-based index
            idx >>= 1  # parent(idx) = idx >> 1 for 1-based index segment tree
            nodes[idx] = mergef(nodes[idx << 1], nodes[(idx << 1) + 1]) # child(idx) = idx << 1 and idx << 1 + 1
    
    def query(self, l, r):
        l += 1; r += 1  # for 1-based index

        l += self.n - 1
        r += self.n - 1
        acc = self.fillval
        while l < r:
            if l & 1:
                acc = self.merge_func(acc, self.nodes[l])
                l += 1
            if r & 1:
                acc = self.merge_func(acc, self.nodes[r - 1])
                r -= 1
            l >>= 1
            r >>= 1
        return acc

def read(t = str):
    return t(sys.stdin.readline().rstrip())

def read_list(t = str, sep = " "):
    return [t(s) for s in sys.stdin.readline().rstrip().split(sep)]

def dprint(*args, **kwargs):
    if DEBUG:
        print(*args, **kwargs)
    return


def main():
    read()
    s = read()

    a_ord = ord("a")
    bit_offsets = {chr(c_ord): c_ord - a_ord for c_ord in range(ord("a"), ord("z") + 1)}

    apps = SegmentTree(len(s), lambda x, y: x | y, 0)
    i = 0
    for c in s:
        apps.update(i, 1 << bit_offsets[c])
        i += 1

    nr_q = read(int)
    for i in range(0, nr_q):
        q = read_list()
        if q[0] == "1":
            _, i, c = q  # i_q in the question starts from 1, not 0
            apps.update(int(i) - 1, 1 << bit_offsets[c])
        else:
            _, l, r = q  # l_q and r_q in the question start from 1, not 0
            print(bin(apps.query(int(l) - 1, int(r) - 1 + 1)).count("1"))  # query in the question includes both edges


if __name__ == "__main__":
    main()

Buggy python submission (verdict: Time Limit Exceeded):
#!/usr/bin/env python3

import sys

DEBUG = False

class SegmentTree:

    def __init__(self, n):
        self.n = 1
        while self.n < n:
            self.n *= 2
        self.fillval = set()
        self.nodes = [self.fillval] * (self.n * 2 - 1)
    
    def update(self, idx, val):
        nodes = self.nodes

        idx += self.n - 1
        nodes[idx] = val
        while idx > 0:
            idx = (idx - 1) // 2
            nodes[idx] = nodes[idx * 2 + 1] | nodes[idx * 2 + 2]
    
    
    def query(self, l, r):
        fillval = self.fillval
        nodes = self.nodes
        
        def _query(l, r, scope_idx, scope_l, scope_r):
            if r <= scope_l or l >= scope_r:
                return fillval
            if l <= scope_l and r >= scope_r:
                return nodes[scope_idx]
            vl = _query(l, r, scope_idx * 2 + 1, scope_l, (scope_l + scope_r) // 2)
            vr = _query(l, r, scope_idx * 2 + 2, (scope_l + scope_r) // 2, scope_r)
            return vl | vr

        return _query(l, r, 0, 0, self.n)


def read(t = str):
    return t(sys.stdin.readline().rstrip())

def read_list(t = str, sep = " "):
    return [t(s) for s in sys.stdin.readline().rstrip().split(sep)]

def dprint(*args, **kwargs):
    if DEBUG:
        print(*args, **kwargs)
    return

class LetterAppearance:
    def __init__(self, chars):
        self.chars_app = SegmentTree(len(chars))
        for i in range(0, len(chars)):
            self.chars_app.update(i, set([chars[i]]))
    
    def replace(self, pos, char):
        self.chars_app.update(pos, set([char]))

    def query(self, l, r):
        return len(self.chars_app.query(l, r))


def main():
    read()
    s = read()
    apps = LetterAppearance(s)
    nr_q = read(int)
    for i in range(0, nr_q):
        q = read_list()
        if q[0] == "1":
            _, i, c = q  # i_q in the question starts from 1, not 0
            apps.replace(int(i) - 1, c)
        else:
            _, l, r = q  # l_q and r_q in the question start from 1, not 0
            print(apps.query(int(l) - 1, int(r) - 1 + 1))  # query in the question includes both edges


if __name__ == "__main__":
    main()

Fixed submission:
#!/usr/bin/env python3

import sys

DEBUG = False


class SegmentTree:

    # Use 1-based index internally
    def __init__(self, n, merge_func, fillval=sys.maxsize):
        self.n = 1
        while self.n < n:
            self.n *= 2
        self.nodes = [fillval] * (self.n * 2 - 1 + 1)  # +1 for 1-based index
        self.merge_func = merge_func
        self.fillval = fillval
    
    def update(self, idx, val):
        idx += 1  # for 1-based index

        nodes = self.nodes
        mergef = self.merge_func

        idx += self.n - 1
        nodes[idx] = val
        while idx > 1: # > 1 for 1-based index
            idx >>= 1  # parent(idx) = idx >> 1 for 1-based index segment tree
            nodes[idx] = mergef(nodes[idx << 1], nodes[(idx << 1) + 1]) # child(idx) = idx << 1 and idx << 1 + 1
    
    def query(self, l, r):
        l += 1; r += 1  # for 1-based index

        l += self.n - 1
        r += self.n - 1
        acc = self.fillval
        while l < r:
            if l & 1:
                acc = self.merge_func(acc, self.nodes[l])
                l += 1
            if r & 1:
                acc = self.merge_func(acc, self.nodes[r - 1])
                r -= 1
            l >>= 1
            r >>= 1
        return acc

def read(t = str):
    return t(sys.stdin.readline().rstrip())

def read_list(t = str, sep = " "):
    return [t(s) for s in sys.stdin.readline().rstrip().split(sep)]

def dprint(*args, **kwargs):
    if DEBUG:
        print(*args, **kwargs)
    return


def main():
    read()
    s = read()

    a_ord = ord("a")
    bit_offsets = {chr(c_ord): c_ord - a_ord for c_ord in range(ord("a"), ord("z") + 1)}

    apps = SegmentTree(len(s), lambda x, y: x | y, 0)
    i = 0
    for c in s:
        apps.update(i, 1 << bit_offsets[c])
        i += 1

    nr_q = read(int)
    for i in range(0, nr_q):
        q = read_list()
        if q[0] == "1":
            _, i, c = q  # i_q in the question starts from 1, not 0
            apps.update(int(i) - 1, 1 << bit_offsets[c])
        else:
            _, l, r = q  # l_q and r_q in the question start from 1, not 0
            print(bin(apps.query(int(l) - 1, int(r) - 1 + 1)).count("1"))  # query in the question includes both edges


if __name__ == "__main__":
    main()

Buggy python submission (verdict: Time Limit Exceeded):
#!/usr/bin/env python3

import sys

DEBUG = False

class SegmentTree:

    def __init__(self, n):
        self.n = 1
        while self.n < n:
            self.n *= 2
        self.fillval = set()
        self.nodes = [self.fillval] * (self.n * 2 - 1)
    
    def update(self, idx, val):
        nodes = self.nodes

        idx += self.n - 1
        nodes[idx] = val
        while idx > 0:
            idx = (idx - 1) // 2
            nodes[idx] = nodes[idx * 2 + 1] | nodes[idx * 2 + 2]
    
    
    def query(self, l, r):
        fillval = self.fillval
        nodes = self.nodes
        
        def _query(l, r, scope_idx, scope_l, scope_r):
            if r <= scope_l or l >= scope_r:
                return fillval
            if l <= scope_l and r >= scope_r:
                return nodes[scope_idx]
            vl = _query(l, r, scope_idx * 2 + 1, scope_l, (scope_l + scope_r) // 2)
            vr = _query(l, r, scope_idx * 2 + 2, (scope_l + scope_r) // 2, scope_r)
            return vl | vr

        return _query(l, r, 0, 0, self.n)


def read(t = str):
    return t(sys.stdin.readline().rstrip())

def read_list(t = str, sep = " "):
    return [t(s) for s in sys.stdin.readline().rstrip().split(sep)]

def dprint(*args, **kwargs):
    if DEBUG:
        print(*args, **kwargs)
    return

class LetterAppearance:
    def __init__(self, chars):
        self.chars_app = SegmentTree(len(chars))
        for i in range(0, len(chars)):
            self.chars_app.update(i, set([chars[i]]))
    
    def replace(self, pos, char):
        self.chars_app.update(pos, set([char]))

    def query(self, l, r):
        return len(self.chars_app.query(l, r))


def main():
    read()
    s = read()
    apps = LetterAppearance(s)
    nr_q = read(int)
    for i in range(0, nr_q):
        q = read_list()
        if q[0] == "1":
            _, i, c = q  # i_q in the question starts from 1, not 0
            apps.replace(int(i) - 1, c)
        else:
            _, l, r = q  # l_q and r_q in the question start from 1, not 0
            print(apps.query(int(l) - 1, int(r) - 1 + 1))  # query in the question includes both edges


if __name__ == "__main__":
    main()

Fixed submission:
#!/usr/bin/env python3

import sys

DEBUG = False


class SegmentTree:

    # Use 1-based index internally
    def __init__(self, n, merge_func, fillval=sys.maxsize):
        self.n = 1
        while self.n < n:
            self.n *= 2
        self.nodes = [fillval] * (self.n * 2 - 1 + 1)  # +1 for 1-based index
        self.merge_func = merge_func
        self.fillval = fillval
    
    def update(self, idx, val):
        idx += 1  # for 1-based index

        nodes = self.nodes
        mergef = self.merge_func

        idx += self.n - 1
        nodes[idx] = val
        while idx > 1: # > 1 for 1-based index
            idx >>= 1  # parent(idx) = idx >> 1 for 1-based index segment tree
            nodes[idx] = mergef(nodes[idx << 1], nodes[(idx << 1) + 1]) # child(idx) = idx << 1 and idx << 1 + 1
    
    def query(self, l, r):
        l += 1; r += 1  # for 1-based index

        l += self.n - 1
        r += self.n - 1
        acc = self.fillval
        while l < r:
            if l & 1:
                acc = self.merge_func(acc, self.nodes[l])
                l += 1
            if r & 1:
                acc = self.merge_func(acc, self.nodes[r - 1])
                r -= 1
            l >>= 1
            r >>= 1
        return acc

def read(t = str):
    return t(sys.stdin.readline().rstrip())

def read_list(t = str, sep = " "):
    return [t(s) for s in sys.stdin.readline().rstrip().split(sep)]

def dprint(*args, **kwargs):
    if DEBUG:
        print(*args, **kwargs)
    return


def main():
    read()
    s = read()

    a_ord = ord("a")
    bit_offsets = {chr(c_ord): c_ord - a_ord for c_ord in range(ord("a"), ord("z") + 1)}

    apps = SegmentTree(len(s), lambda x, y: x | y, 0)
    i = 0
    for c in s:
        apps.update(i, 1 << bit_offsets[c])
        i += 1

    nr_q = read(int)
    for i in range(0, nr_q):
        q = read_list()
        if q[0] == "1":
            _, i, c = q  # i_q in the question starts from 1, not 0
            apps.update(int(i) - 1, 1 << bit_offsets[c])
        else:
            _, l, r = q  # l_q and r_q in the question start from 1, not 0
            print(bin(apps.query(int(l) - 1, int(r) - 1 + 1)).count("1"))  # query in the question includes both edges


if __name__ == "__main__":
    main()

Buggy python submission (verdict: Time Limit Exceeded):
#!/usr/bin/env python3

import sys

DEBUG = False

class SegmentTree:
    def __init__(self, n, merge_func, fillval=sys.maxsize):
        self.n = 1
        while self.n < n:
            self.n *= 2
        self.nodes = [fillval] * (self.n * 2 - 1)
        self.merge_func = merge_func
        self.fillval = fillval
    
    def update(self, idx, val):
        nodes = self.nodes
        mergef = self.merge_func

        idx += self.n - 1
        nodes[idx] = val
        while idx > 0:
            idx = (idx - 1) // 2
            nodes[idx] = mergef(nodes[idx * 2 + 1], nodes[idx * 2 + 2])
    
    
    def query(self, l, r):
        fillval = self.fillval
        nodes = self.nodes
        mergef = self.merge_func
        
        def _query(l, r, scope_idx, scope_l, scope_r):
            if r <= scope_l or l >= scope_r:
                return fillval
            if l <= scope_l and r >= scope_r:
                return nodes[scope_idx]
            vl = _query(l, r, scope_idx * 2 + 1, scope_l, (scope_l + scope_r) // 2)
            vr = _query(l, r, scope_idx * 2 + 2, (scope_l + scope_r) // 2, scope_r)
            return mergef(vl, vr)

        return _query(l, r, 0, 0, self.n)


def read(t = str):
    return t(sys.stdin.readline().rstrip())

def read_list(t = str, sep = " "):
    return [t(s) for s in sys.stdin.readline().rstrip().split(sep)]

def dprint(*args, **kwargs):
    if DEBUG:
        print(*args, **kwargs)
    return

class LetterAppearance:
    def __init__(self, chars):
        self.chars = chars
        self.chars_app = SegmentTree(len(chars), lambda x, y: x.union(y), set())
        for i in range(0, len(chars)):
            self.chars_app.update(i, set([chars[i]]))
    
    def replace(self, pos, char):
        if self.chars[pos] == char:
            return
        self.chars_app.update(pos, set([char]))
        self.chars[pos] = char

    def query(self, l, r):
        return len(self.chars_app.query(l, r))


def main():
    read()
    s = read()
    apps = LetterAppearance(list(s))
    nr_q = read(int)
    for i in range(0, nr_q):
        q = read_list()
        if q[0] == "1":
            _, i, c = q  # i_q in the question starts from 1, not 0
            apps.replace(int(i) - 1, c)
        else:
            _, l, r = q  # l_q and r_q in the question start from 1, not 0
            print(apps.query(int(l) - 1, int(r) - 1 + 1))  # query in the question includes both edges


if __name__ == "__main__":
    main()

Fixed submission:
#!/usr/bin/env python3

import sys

DEBUG = False


class SegmentTree:

    # Use 1-based index internally
    def __init__(self, n, merge_func, fillval=sys.maxsize):
        self.n = 1
        while self.n < n:
            self.n *= 2
        self.nodes = [fillval] * (self.n * 2 - 1 + 1)  # +1 for 1-based index
        self.merge_func = merge_func
        self.fillval = fillval
    
    def update(self, idx, val):
        idx += 1  # for 1-based index

        nodes = self.nodes
        mergef = self.merge_func

        idx += self.n - 1
        nodes[idx] = val
        while idx > 1: # > 1 for 1-based index
            idx >>= 1  # parent(idx) = idx >> 1 for 1-based index segment tree
            nodes[idx] = mergef(nodes[idx << 1], nodes[(idx << 1) + 1]) # child(idx) = idx << 1 and idx << 1 + 1
    
    def query(self, l, r):
        l += 1; r += 1  # for 1-based index

        l += self.n - 1
        r += self.n - 1
        acc = self.fillval
        while l < r:
            if l & 1:
                acc = self.merge_func(acc, self.nodes[l])
                l += 1
            if r & 1:
                acc = self.merge_func(acc, self.nodes[r - 1])
                r -= 1
            l >>= 1
            r >>= 1
        return acc

def read(t = str):
    return t(sys.stdin.readline().rstrip())

def read_list(t = str, sep = " "):
    return [t(s) for s in sys.stdin.readline().rstrip().split(sep)]

def dprint(*args, **kwargs):
    if DEBUG:
        print(*args, **kwargs)
    return


def main():
    read()
    s = read()

    a_ord = ord("a")
    bit_offsets = {chr(c_ord): c_ord - a_ord for c_ord in range(ord("a"), ord("z") + 1)}

    apps = SegmentTree(len(s), lambda x, y: x | y, 0)
    i = 0
    for c in s:
        apps.update(i, 1 << bit_offsets[c])
        i += 1

    nr_q = read(int)
    for i in range(0, nr_q):
        q = read_list()
        if q[0] == "1":
            _, i, c = q  # i_q in the question starts from 1, not 0
            apps.update(int(i) - 1, 1 << bit_offsets[c])
        else:
            _, l, r = q  # l_q and r_q in the question start from 1, not 0
            print(bin(apps.query(int(l) - 1, int(r) - 1 + 1)).count("1"))  # query in the question includes both edges


if __name__ == "__main__":
    main()

Buggy python submission (verdict: Runtime Error):
#!/usr/bin/env python3

import sys

DEBUG = False

class SegmentTree:
    def __init__(self, n, merge_func, fillval=sys.maxsize):
        self.n = 1
        while self.n < n:
            self.n *= 2
        self.nodes = [fillval] * (self.n * 2 - 1)
        self.merge_func = merge_func
        self.fillval = fillval
    
    def update(self, idx, val):
        nodes = self.nodes
        mergef = self.merge_func

        idx += self.n - 1
        nodes[idx] = val
        while idx > 0:
            idx = (idx - 1) // 2
            nodes[idx] = mergef(nodes[idx * 2 + 1], nodes[idx * 2 + 2])
    
    
    def query(self, l, r):
        fillval = self.fillval
        nodes = self.nodes
        mergef = self.merge_func
        
        def _query(l, r, scope_idx, scope_l, scope_r):
            if r <= scope_l or l >= scope_r:
                return fillval
            if l <= scope_l and r >= scope_r:
                return nodes[scope_idx]
            vl = _query(l, r, scope_idx * 2 + 1, scope_l, (scope_l + scope_r) // 2)
            vr = _query(l, r, scope_idx * 2 + 2, (scope_l + scope_r) // 2, scope_r)
            return mergef(vl, vr)

        return _query(l, r, 0, 0, self.n)


def read(t = str):
    return t(sys.stdin.readline().rstrip())

def read_list(t = str, sep = " "):
    return [t(s) for s in sys.stdin.readline().rstrip().split(sep)]

def dprint(*args, **kwargs):
    if DEBUG:
        print(*args, **kwargs)
    return

class LetterAppearance:
    def __init__(self, chars):
        self.chars = chars
        self.chars_app = SegmentTree(len(chars), lambda x, y: x.union(y), set())
        for i in range(0, len(chars)):
            self.chars_app.update(i, set([chars[i]]))
    
    def replace(self, pos, char):
        if self.chars[pos] == char:
            return
        self.chars_app.update(pos, set([char]))
        self.chars[pos] = char

    def query(self, l, r):
        return len(self.chars_app.query(l, r))


def main():
    read()
    s = read()
    apps = LetterAppearance(s)
    nr_q = read(int)
    for i in range(0, nr_q):
        q = read_list()
        if q[0] == "1":
            _, i, c = q  # i_q in the question starts from 1, not 0
            apps.replace(int(i) - 1, c)
        else:
            _, l, r = q  # l_q and r_q in the question start from 1, not 0
            print(apps.query(int(l) - 1, int(r) - 1 + 1))  # query in the question includes both edges


if __name__ == "__main__":
    main()

Fixed submission:
#!/usr/bin/env python3

import sys

DEBUG = False


class SegmentTree:

    # Use 1-based index internally
    def __init__(self, n, merge_func, fillval=sys.maxsize):
        self.n = 1
        while self.n < n:
            self.n *= 2
        self.nodes = [fillval] * (self.n * 2 - 1 + 1)  # +1 for 1-based index
        self.merge_func = merge_func
        self.fillval = fillval
    
    def update(self, idx, val):
        idx += 1  # for 1-based index

        nodes = self.nodes
        mergef = self.merge_func

        idx += self.n - 1
        nodes[idx] = val
        while idx > 1: # > 1 for 1-based index
            idx >>= 1  # parent(idx) = idx >> 1 for 1-based index segment tree
            nodes[idx] = mergef(nodes[idx << 1], nodes[(idx << 1) + 1]) # child(idx) = idx << 1 and idx << 1 + 1
    
    def query(self, l, r):
        l += 1; r += 1  # for 1-based index

        l += self.n - 1
        r += self.n - 1
        acc = self.fillval
        while l < r:
            if l & 1:
                acc = self.merge_func(acc, self.nodes[l])
                l += 1
            if r & 1:
                acc = self.merge_func(acc, self.nodes[r - 1])
                r -= 1
            l >>= 1
            r >>= 1
        return acc

def read(t = str):
    return t(sys.stdin.readline().rstrip())

def read_list(t = str, sep = " "):
    return [t(s) for s in sys.stdin.readline().rstrip().split(sep)]

def dprint(*args, **kwargs):
    if DEBUG:
        print(*args, **kwargs)
    return


def main():
    read()
    s = read()

    a_ord = ord("a")
    bit_offsets = {chr(c_ord): c_ord - a_ord for c_ord in range(ord("a"), ord("z") + 1)}

    apps = SegmentTree(len(s), lambda x, y: x | y, 0)
    i = 0
    for c in s:
        apps.update(i, 1 << bit_offsets[c])
        i += 1

    nr_q = read(int)
    for i in range(0, nr_q):
        q = read_list()
        if q[0] == "1":
            _, i, c = q  # i_q in the question starts from 1, not 0
            apps.update(int(i) - 1, 1 << bit_offsets[c])
        else:
            _, l, r = q  # l_q and r_q in the question start from 1, not 0
            print(bin(apps.query(int(l) - 1, int(r) - 1 + 1)).count("1"))  # query in the question includes both edges


if __name__ == "__main__":
    main()

Buggy python submission (verdict: Time Limit Exceeded):
import sys

DEBUG = False

class SegmentTree:
    def __init__(self, n, merge_func, fillval=sys.maxsize):
        self.n = 1
        while self.n < n:
            self.n *= 2
        self.nodes = [fillval] * (self.n * 2 - 1)
        self.merge_func = merge_func
        self.fillval = fillval
    
    def update(self, idx, val):
        idx += self.n - 1
        self.nodes[idx] = val
        while idx > 0:
            idx = (idx - 1) // 2
            self.nodes[idx] = self.merge_func(self.nodes[idx * 2 + 1], self.nodes[idx * 2 + 2])
    
    def _query(self, l, r, scope_idx, scope_l, scope_r):
        if r <= scope_l or l >= scope_r:
            return self.fillval
        if l <= scope_l and r >= scope_r:
            return self.nodes[scope_idx]
        vl = self._query(l, r, scope_idx * 2 + 1, scope_l, (scope_l + scope_r) // 2)
        vr = self._query(l, r, scope_idx * 2 + 2, (scope_l + scope_r) // 2, scope_r)
        return self.merge_func(vl, vr)
    
    def query(self, l, r):
        return self._query(l, r, 0, 0, self.n)


def read(t = str):
    return t(sys.stdin.readline().rstrip())

def read_list(t = str, sep = " "):
    return [t(s) for s in sys.stdin.readline().rstrip().split(sep)]

def dprint(*args, **kwargs):
    if DEBUG:
        print(*args, **kwargs)
    return

class LetterAppearance:
    def __init__(self, chars):
        self.chars = chars
        self.chars_app = SegmentTree(len(chars), lambda x, y: x.union(y), set())
        for i in range(0, len(chars)):
            self.chars_app.update(i, set([chars[i]]))
    
    def replace(self, pos, char):
        if self.chars[pos] == char:
            return
        self.chars_app.update(pos, set([char]))
        self.chars[pos] = char

    def query(self, l, r):
        return len(self.chars_app.query(l, r))


def main():
    read()
    s = read()
    apps = LetterAppearance(list(s))
    nr_q = read(int)
    for i in range(0, nr_q):
        q = read_list()
        if q[0] == "1":
            _, i, c = q  # i_q in the question starts from 1, not 0
            apps.replace(int(i) - 1, c)
        else:
            _, l, r = q  # l_q and r_q in the question start from 1, not 0
            print(apps.query(int(l) - 1, int(r) - 1 + 1))  # query in the question includes both edges


if __name__ == "__main__":
    main()

Fixed submission:
#!/usr/bin/env python3

import sys

DEBUG = False


class SegmentTree:

    # Use 1-based index internally
    def __init__(self, n, merge_func, fillval=sys.maxsize):
        self.n = 1
        while self.n < n:
            self.n *= 2
        self.nodes = [fillval] * (self.n * 2 - 1 + 1)  # +1 for 1-based index
        self.merge_func = merge_func
        self.fillval = fillval
    
    def update(self, idx, val):
        idx += 1  # for 1-based index

        nodes = self.nodes
        mergef = self.merge_func

        idx += self.n - 1
        nodes[idx] = val
        while idx > 1: # > 1 for 1-based index
            idx >>= 1  # parent(idx) = idx >> 1 for 1-based index segment tree
            nodes[idx] = mergef(nodes[idx << 1], nodes[(idx << 1) + 1]) # child(idx) = idx << 1 and idx << 1 + 1
    
    def query(self, l, r):
        l += 1; r += 1  # for 1-based index

        l += self.n - 1
        r += self.n - 1
        acc = self.fillval
        while l < r:
            if l & 1:
                acc = self.merge_func(acc, self.nodes[l])
                l += 1
            if r & 1:
                acc = self.merge_func(acc, self.nodes[r - 1])
                r -= 1
            l >>= 1
            r >>= 1
        return acc

def read(t = str):
    return t(sys.stdin.readline().rstrip())

def read_list(t = str, sep = " "):
    return [t(s) for s in sys.stdin.readline().rstrip().split(sep)]

def dprint(*args, **kwargs):
    if DEBUG:
        print(*args, **kwargs)
    return


def main():
    read()
    s = read()

    a_ord = ord("a")
    bit_offsets = {chr(c_ord): c_ord - a_ord for c_ord in range(ord("a"), ord("z") + 1)}

    apps = SegmentTree(len(s), lambda x, y: x | y, 0)
    i = 0
    for c in s:
        apps.update(i, 1 << bit_offsets[c])
        i += 1

    nr_q = read(int)
    for i in range(0, nr_q):
        q = read_list()
        if q[0] == "1":
            _, i, c = q  # i_q in the question starts from 1, not 0
            apps.update(int(i) - 1, 1 << bit_offsets[c])
        else:
            _, l, r = q  # l_q and r_q in the question start from 1, not 0
            print(bin(apps.query(int(l) - 1, int(r) - 1 + 1)).count("1"))  # query in the question includes both edges


if __name__ == "__main__":
    main()

Buggy python submission (verdict: Time Limit Exceeded):
import sys

DEBUG = False

class SegmentTree:
    def __init__(self, n, merge_func, fillval=sys.maxsize):
        self.n = 1
        while self.n < n:
            self.n *= 2
        self.nodes = [fillval] * (self.n * 2 - 1)
        self.merge_func = merge_func
        self.fillval = fillval
    
    def update(self, idx, val):
        idx += self.n - 1
        self.nodes[idx] = val
        while idx > 0:
            idx = (idx - 1) // 2
            self.nodes[idx] = self.merge_func(self.nodes[idx * 2 + 1], self.nodes[idx * 2 + 2])
    
    def _query(self, l, r, scope_idx, scope_l, scope_r):
        if r <= scope_l or l >= scope_r:
            return self.fillval
        if l <= scope_l and r >= scope_r:
            return self.nodes[scope_idx]
        vl = self._query(l, r, scope_idx * 2 + 1, scope_l, (scope_l + scope_r) // 2)
        vr = self._query(l, r, scope_idx * 2 + 2, (scope_l + scope_r) // 2, scope_r)
        return self.merge_func(vl, vr)
    
    def query(self, l, r):
        return self._query(l, r, 0, 0, self.n)


def read(t = str):
    return t(sys.stdin.readline().rstrip())

def read_list(t = str, sep = " "):
    return [t(s) for s in sys.stdin.readline().rstrip().split(sep)]

def dprint(*args, **kwargs):
    if DEBUG:
        print(*args, **kwargs)
    return

class LetterAppearance:
    def __init__(self, chars):
        self.chars = chars
        self.chars_app = SegmentTree(len(chars), lambda x, y: x.union(y), set())
        for i in range(0, len(chars)):
            self.chars_app.update(i, set([chars[i]]))
    
    def replace(self, pos, char):
        if self.chars[pos] == char:
            return
        self.chars_app.update(pos, set([char]))
        self.chars[pos] = char

    def query(self, l, r):
        return len(self.chars_app.query(l, r))


def main():
    read()
    s = read()
    apps = LetterAppearance(list(s))
    nr_q = read(int)
    for i in range(0, nr_q):
        q = read_list()
        if q[0] == "1":
            _, i, c = q  # i_q in the question starts from 1, not 0
            apps.replace(int(i) - 1, c)
        else:
            _, l, r = q  # l_q and r_q in the question start from 1, not 0
            print(apps.query(int(l) - 1, int(r) - 1 + 1))  # query in the question includes both edges


if __name__ == "__main__":
    main()

Fixed submission:
#!/usr/bin/env python3

import sys

DEBUG = False


class SegmentTree:

    # Use 1-based index internally
    def __init__(self, n, merge_func, fillval=sys.maxsize):
        self.n = 1
        while self.n < n:
            self.n *= 2
        self.nodes = [fillval] * (self.n * 2 - 1 + 1)  # +1 for 1-based index
        self.merge_func = merge_func
        self.fillval = fillval
    
    def update(self, idx, val):
        idx += 1  # for 1-based index

        nodes = self.nodes
        mergef = self.merge_func

        idx += self.n - 1
        nodes[idx] = val
        while idx > 1: # > 1 for 1-based index
            idx >>= 1  # parent(idx) = idx >> 1 for 1-based index segment tree
            nodes[idx] = mergef(nodes[idx << 1], nodes[(idx << 1) + 1]) # child(idx) = idx << 1 and idx << 1 + 1
    
    def query(self, l, r):
        l += 1; r += 1  # for 1-based index

        l += self.n - 1
        r += self.n - 1
        acc = self.fillval
        while l < r:
            if l & 1:
                acc = self.merge_func(acc, self.nodes[l])
                l += 1
            if r & 1:
                acc = self.merge_func(acc, self.nodes[r - 1])
                r -= 1
            l >>= 1
            r >>= 1
        return acc

def read(t = str):
    return t(sys.stdin.readline().rstrip())

def read_list(t = str, sep = " "):
    return [t(s) for s in sys.stdin.readline().rstrip().split(sep)]

def dprint(*args, **kwargs):
    if DEBUG:
        print(*args, **kwargs)
    return


def main():
    read()
    s = read()

    a_ord = ord("a")
    bit_offsets = {chr(c_ord): c_ord - a_ord for c_ord in range(ord("a"), ord("z") + 1)}

    apps = SegmentTree(len(s), lambda x, y: x | y, 0)
    i = 0
    for c in s:
        apps.update(i, 1 << bit_offsets[c])
        i += 1

    nr_q = read(int)
    for i in range(0, nr_q):
        q = read_list()
        if q[0] == "1":
            _, i, c = q  # i_q in the question starts from 1, not 0
            apps.update(int(i) - 1, 1 << bit_offsets[c])
        else:
            _, l, r = q  # l_q and r_q in the question start from 1, not 0
            print(bin(apps.query(int(l) - 1, int(r) - 1 + 1)).count("1"))  # query in the question includes both edges


if __name__ == "__main__":
    main()

Buggy python submission (verdict: Time Limit Exceeded):
import sys

DEBUG = False

class SegmentTree:
    def __init__(self, n, merge_func, fillval=sys.maxsize):
        self.n = 1
        while self.n < n:
            self.n *= 2
        self.nodes = [fillval] * (self.n * 2 - 1)
        self.merge_func = merge_func
        self.fillval = fillval
    
    def update(self, idx, val):
        idx += self.n - 1
        self.nodes[idx] = val
        while idx > 0:
            idx = (idx - 1) // 2
            self.nodes[idx] = self.merge_func(self.nodes[idx * 2 + 1], self.nodes[idx * 2 + 2])
    
    def _query(self, l, r, scope_idx, scope_l, scope_r):
        if r <= scope_l or l >= scope_r:
            return self.fillval
        if l <= scope_l and r >= scope_r:
            return self.nodes[scope_idx]
        vl = self._query(l, r, scope_idx * 2 + 1, scope_l, (scope_l + scope_r) // 2)
        vr = self._query(l, r, scope_idx * 2 + 2, (scope_l + scope_r) // 2, scope_r)
        return self.merge_func(vl, vr)
    
    def query(self, l, r):
        return self._query(l, r, 0, 0, self.n)


def read(t = str):
    return t(sys.stdin.readline().rstrip())

def read_list(t = str, sep = " "):
    return [t(s) for s in sys.stdin.readline().rstrip().split(sep)]

def dprint(*args, **kwargs):
    if DEBUG:
        print(*args, **kwargs)
    return

class LetterAppearance:
    def __init__(self, chars):
        self.chars = chars
        self.chars_app = {}
        for c_ord in range(ord("a"), ord("z") + 1):
            c = chr(c_ord)
            self.chars_app[c] = SegmentTree(len(chars), lambda x, y: max([x, y]), 0)
        for i in range(0, len(chars)):
            c = chars[i]
            self.chars_app[c].update(i, 1)
    
    def replace(self, pos, char):
        if self.chars[pos] == char:
            return
        self.chars_app[self.chars[pos]].update(pos, 0)
        self.chars_app[char].update(pos, 1)
        self.chars[pos] = char

    def query(self, l, r):
        return sum((self.chars_app[chr(c_ord)].query(l, r) for c_ord in range(ord("a"), ord("z") + 1)))


def main():
    read()
    s = read()
    apps = LetterAppearance(list(s))
    nr_q = read(int)
    for i in range(0, nr_q):
        q = read_list()
        if q[0] == "1":
            _, i, c = q  # i_q in the question starts from 1, not 0
            apps.replace(int(i) - 1, c)
        else:
            _, l, r = q  # l_q and r_q in the question start from 1, not 0
            print(apps.query(int(l) - 1, int(r) - 1 + 1))  # query in the question includes both edges


if __name__ == "__main__":
    main()

Fixed submission:
#!/usr/bin/env python3

import sys

DEBUG = False


class SegmentTree:

    # Use 1-based index internally
    def __init__(self, n, merge_func, fillval=sys.maxsize):
        self.n = 1
        while self.n < n:
            self.n *= 2
        self.nodes = [fillval] * (self.n * 2 - 1 + 1)  # +1 for 1-based index
        self.merge_func = merge_func
        self.fillval = fillval
    
    def update(self, idx, val):
        idx += 1  # for 1-based index

        nodes = self.nodes
        mergef = self.merge_func

        idx += self.n - 1
        nodes[idx] = val
        while idx > 1: # > 1 for 1-based index
            idx >>= 1  # parent(idx) = idx >> 1 for 1-based index segment tree
            nodes[idx] = mergef(nodes[idx << 1], nodes[(idx << 1) + 1]) # child(idx) = idx << 1 and idx << 1 + 1
    
    def query(self, l, r):
        l += 1; r += 1  # for 1-based index

        l += self.n - 1
        r += self.n - 1
        acc = self.fillval
        while l < r:
            if l & 1:
                acc = self.merge_func(acc, self.nodes[l])
                l += 1
            if r & 1:
                acc = self.merge_func(acc, self.nodes[r - 1])
                r -= 1
            l >>= 1
            r >>= 1
        return acc

def read(t = str):
    return t(sys.stdin.readline().rstrip())

def read_list(t = str, sep = " "):
    return [t(s) for s in sys.stdin.readline().rstrip().split(sep)]

def dprint(*args, **kwargs):
    if DEBUG:
        print(*args, **kwargs)
    return


def main():
    read()
    s = read()

    a_ord = ord("a")
    bit_offsets = {chr(c_ord): c_ord - a_ord for c_ord in range(ord("a"), ord("z") + 1)}

    apps = SegmentTree(len(s), lambda x, y: x | y, 0)
    i = 0
    for c in s:
        apps.update(i, 1 << bit_offsets[c])
        i += 1

    nr_q = read(int)
    for i in range(0, nr_q):
        q = read_list()
        if q[0] == "1":
            _, i, c = q  # i_q in the question starts from 1, not 0
            apps.update(int(i) - 1, 1 << bit_offsets[c])
        else:
            _, l, r = q  # l_q and r_q in the question start from 1, not 0
            print(bin(apps.query(int(l) - 1, int(r) - 1 + 1)).count("1"))  # query in the question includes both edges


if __name__ == "__main__":
    main()