Problem:
Score : 500 points

Problem Statement

You are given a string S of length N consisting of lowercase English letters.

Process Q queries of the following two types:

Type 1: change the i_q-th character of S to c_q. (Do nothing if the i_q-th character is already c_q.)

Type 2: answer the number of different characters occurring in the substring of S between the l_q-th and r_q-th characters (inclusive).

Constraints

N, Q, i_q, l_q, and r_q are integers.

S is a string consisting of lowercase English letters.

c_q is a lowercase English letter.

1 \leq N \leq 500000

1 \leq Q \leq 20000

|S| = N

1 \leq i_q \leq N

1 \leq l_q \leq r_q \leq N

There is at least one query of type 2 in each testcase.

Input

Input is given from Standard Input in the following format:

N
S
Q
Query_1
\vdots
Query_Q

Here, Query_i in the 4-th through (Q+3)-th lines is one of the following:

1 i_q c_q

2 l_q r_q

Output

For each query of type 2, print a line containing the answer.

Sample Input 1

7
abcdbbd
6
2 3 6
1 5 z
2 1 1
1 4 a
1 7 d
2 1 7

Sample Output 1

3
1
5

In the first query, cdbb contains three kinds of letters: b , c , and d, so we print 3.

In the second query, S is modified to abcdzbd.

In the third query, a contains one kind of letter: a, so we print 1.

In the fourth query, S is modified to abcazbd.

In the fifth query, S does not change and is still abcazbd.

In the sixth query, abcazbd contains five kinds of letters: a, b, c, d, and z, so we print 5.

Solution in C# (Mono 4.6.2.0) (judge verdict: Accepted)

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace AtCoder
{
    public static class Ex
    {
        public static List<string> FastSort(this List<string> s) { s.Sort(StringComparer.OrdinalIgnoreCase); return s.ToList(); }
        public static string yesno(this bool b) { return b ? "yes" : "no"; }
        public static string YesNo(this bool b) { return b ? "Yes" : "No"; }
        public static string YESNO(this bool b) { return b ? "YES" : "NO"; }
    }

    partial class AtCoder
    {
        public string GetStr() { return Console.ReadLine().Trim(); }
        public char GetChar() { return Console.ReadLine().Trim()[0]; }
        public int GetInt() { return int.Parse(Console.ReadLine().Trim()); }
        public long GetLong() { return long.Parse(Console.ReadLine().Trim()); }
        public double GetDouble() { return double.Parse(Console.ReadLine().Trim()); }
        public string[] GetStrArray() { return Console.ReadLine().Trim().Split(' '); }
        public int[] GetIntArray() { return Console.ReadLine().Trim().Split(' ').Select(int.Parse).ToArray(); }
        public long[] GetLongArray() { return Console.ReadLine().Trim().Split(' ').Select(long.Parse).ToArray(); }
        public char[] GetCharArray() { return Console.ReadLine().Trim().Split(' ').Select(char.Parse).ToArray(); }
        public double[] GetDoubleArray() { return Console.ReadLine().Trim().Split(' ').Select(double.Parse).ToArray(); }
        public T[][] CreateJaggedArray<T>(int H, int W, T value) { return Enumerable.Repeat(0, H).Select(s => Enumerable.Repeat(value, W).ToArray()).ToArray(); }
        public int[][] GetIntJaggedArray(int N) { return Enumerable.Repeat(0, N).Select(s => GetIntArray().ToArray()).ToArray(); }
        public long[][] GetLongJaggedArray(int N) { return Enumerable.Repeat(0, N).Select(s => GetLongArray().ToArray()).ToArray(); }
        public char[][] GetCharJaggedArray(int N) { return Enumerable.Repeat(0, N).Select(s => GetStr().ToCharArray()).ToArray(); }
        public double[][] GetDoubleJaggedArray(int N) { return Enumerable.Repeat(0, N).Select(s => GetDoubleArray()).ToArray(); }

        Dictionary<int, List<int>> GetUnweightedAdjacencyList(int N, int M, bool isDirected, bool isNode_0indexed)
        {
            var dic = new Dictionary<int, List<int>>();
            foreach (var e in Enumerable.Range(0, N)) { dic.Add(e, new List<int>()); }
            for (int i = 0; i < M; i++)
            {
                var input = GetIntArray();
                var a = isNode_0indexed ? input[0] : input[0] - 1;
                var b = isNode_0indexed ? input[1] : input[1] - 1;
                dic[a].Add(b);
                if (isDirected == false) dic[b].Add(a);
            }
            return dic;
        }
        Dictionary<int, List<Edge>> GetWeightedAdjacencyList(int N, int M, bool isDirected, bool isNode_0indexed)
        {
            var dic = new Dictionary<int, List<Edge>>();
            foreach (var e in Enumerable.Range(0, N)) { dic.Add(e, new List<Edge>()); }
            for (int i = 0; i < M; i++)
            {
                var input = GetIntArray();
                var a = isNode_0indexed ? input[0] : input[0] - 1;
                var b = isNode_0indexed ? input[1] : input[1] - 1;
                var c = input[2];
                dic[a].Add(new Edge(b, c));
                if (isDirected == false) dic[b].Add(new Edge(a, c));
            }
            return dic;
        }

        bool eq<T, U>() => typeof(T).Equals(typeof(U));
        T ct<T, U>(U a) => (T)Convert.ChangeType(a, typeof(T));
        T cv<T>(string s) => eq<T, int>() ? ct<T, int>(int.Parse(s))
                           : eq<T, long>() ? ct<T, long>(long.Parse(s))
                           : eq<T, double>() ? ct<T, double>(double.Parse(s))
                           : eq<T, char>() ? ct<T, char>(s[0])
                                             : ct<T, string>(s);
        void Multi<T>(out T a) => a = cv<T>(GetStr());
        void Multi<T, U>(out T a, out U b)
        {
            var ar = GetStrArray(); a = cv<T>(ar[0]); b = cv<U>(ar[1]);
        }
        void Multi<T, U, V>(out T a, out U b, out V c)
        {
            var ar = GetStrArray(); a = cv<T>(ar[0]); b = cv<U>(ar[1]); c = cv<V>(ar[2]);
        }
        void Multi<T, U, V, W>(out T a, out U b, out V c, out W d)
        {
            var ar = GetStrArray(); a = cv<T>(ar[0]); b = cv<U>(ar[1]); c = cv<V>(ar[2]); d = cv<W>(ar[3]);
        }
        void Multi<T, U, V, W, X>(out T a, out U b, out V c, out W d, out X e)
        {
            var ar = GetStrArray(); a = cv<T>(ar[0]); b = cv<U>(ar[1]); c = cv<V>(ar[2]); d = cv<W>(ar[3]); e = cv<X>(ar[4]);
        }
        void Multi<T, U, V, W, X, Y>(out T a, out U b, out V c, out W d, out X e, out Y f)
        {
            var ar = GetStrArray(); a = cv<T>(ar[0]); b = cv<U>(ar[1]); c = cv<V>(ar[2]); d = cv<W>(ar[3]); e = cv<X>(ar[4]); f = cv<Y>(ar[5]);
        }
    }

    class Edge
    {
        public Edge(int to, long length)
        {
            To = to;
            Length = length;
        }
        public int To { get; set; }
        public long Length { get; set; }
    }



    partial class Program
    {
        static void Main()
        {
            Console.SetOut(new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = false });
            new AtCoder().Solve();
            Console.Out.Flush();
            Console.Read();
        }
    }

    public partial class AtCoder
    {
        public void Solve()
        {
            int N = GetInt();
            string S = GetStr();
            var st = new int[26].Select(s => new SegmentTree<bool>(N, (bool a, bool b) => (a || b), false)).ToArray();
            for (int i = 0; i < N; i++)
            {
                st[S[i] - 'a'].Set(i, true);
            }
            for (int i = 0; i < 26; i++)
            {
                st[i].Build();
            }

            var Q = GetInt();
            for (int i = 0; i < Q; i++)
            {
                var query = GetStrArray();
                if (query[0] == "1")
                {
                    var iq = int.Parse(query[1])-1;
                    var cq = query[2].First();
                    for (int j = 0; j < 26; j++)
                    {
                        st[j].Update(iq, false);
                    }
                    st[cq - 'a'].Update(iq, true);
                }
                else
                {
                    var lq = int.Parse(query[1])-1;
                    var rq = int.Parse(query[2])-1;
                    Console.WriteLine(st.Select(s => s.RangeQuery(lq, rq+1)).Count(c => c));
                }
            }


        }
    }

    class SegmentTree<T>
    {
        /// <summary>
        /// セグ木のコンストラクタ
        /// </summary>
        /// <param name="_n">数列の長さ</param>
        /// <param name="_func">モノイドに行う処理</param>
        /// <param name="_gen">モノイドの単位限</param>
        public SegmentTree(int _n, Func<T, T, T> _func, T _gen)
        {
            n = (int)Math.Pow(2,Math.Floor(Math.Log(_n, 2)) + 1);
            Data = new T[2 * n - 1].Select(s => _gen).ToArray();
            func = _func;
            gen = _gen;
        }

        readonly int n;
        public T[] Data { get; private set; }
        readonly Func<T, T, T> func;
        readonly T gen;

        /// <summary>
        /// セグ木の葉に要素をセットする
        /// </summary>
        /// <param name="i">数列の位置</param>
        /// <param name="x">セットする要素</param>
        public void Set(int i, T x)
        {
            Data[i + n - 1] = x;
        }

        /// <summary>
        /// セグ木を構築する。
        /// </summary>
        public void Build()
        {
            for (int k = n - 2; k >= 0; k--)
            {
                Data[k] = func(Data[2 * k + 1], Data[2 * k + 2]);
            }
        }

        /// <summary>
        /// 要素の更新を行う
        /// </summary>
        /// <param name="i">更新を行う位置</param>
        /// <param name="x">更新する値</param>
        public void Update(int i, T x)
        {
            i += n - 1;
            Data[i] = x;
            while (i > 0)
            {
                i = (i - 1) / 2;
                Data[i] = func(Data[i * 2 + 1], Data[i * 2 + 2]);
            }
        }

        /// <summary>
        /// 区間[a,b)でfuncを実行した結果
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public T RangeQuery(int a, int b)
        {
            return RangeQuerySub(a, b, 0, 0, n);
        }

        T RangeQuerySub(int a, int b, int k, int l, int r)
        {
            if (r <= a || b <= l) return gen;
            else if (a <= l && r <= b) return Data[k];
            else
            {
                T vl = RangeQuerySub(a, b, k * 2 + 1, l, (l + r) / 2);
                T vr = RangeQuerySub(a, b, k * 2 + 2, (l + r) / 2, r);
                return func(vl, vr);
            }
        }
    }
}