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 PHP7 (7.0.15) (judge verdict: Wrong Answer)

<?php

class Node {
  public $data;
  public $leftNode;
  public $rightNode;
  function __construct($data){
    $this->data = $data;
    $this->leftNode = null;
    $this->rightNode = null;
  }
}

class BinaryTree {

  private $root;

  function insert($data){
    $p =& $this->root;
    while($p != null){
      if($p->data == $data) return false;
      if($p->data > $data){
        $p =& $p->leftNode;
      }else if($p->data < $data){
        $p =& $p->rightNode;
      }
    }
    $p = new Node($data);
  }

  function delete($data){
    $p =& $this->root;
    $parent = null;
    while($p != null){
      // データ発見
      if($p->data == $data){
        // 右の最小のノードを検索
        if($p->leftNode != null && $p->rightNode != null){
          $p2 =& $p->rightNode;
          while($p2->leftNode != null){
            $p2 =& $p2->leftNode;
          }
          // データを更新
          $p->data =& $p2->data;
        }        
        // 左だけ
        else if($p->leftNode != null){
          //echo "left ".$parent->data ." ".$data.PHP_EOL;
          if($parent != null){
            if($parent->data > $p->data){
              $parent->leftNode =& $p->leftNode;
            }else{
              $parent->rightNode =& $p->leftNode;
            }
          }
        }
        // 右だけ
        else if($p->rightNode != null){
          //echo "right ".$parent->data ." ".$data.PHP_EOL;
          if($parent != null){
            if($parent->data > $p->data){
              $parent->leftNode =& $p->rightNode;
            }else{
              $parent->rightNode =& $p->rightNode;
            }
          }
        }
        // 子なし
        else {
          //echo "leaf ".$parent->data ." ".$data.PHP_EOL;
          if($parent != null){
            if($parent->data < $data){
              $parent->rightNode = null;
            }else {
              $parent->leftNode = null;
            }
          }
        }
        return true;
      }
      $parent =& $p;
      // 左を検索
      if($p->data > $data)
      {
        $p =& $p->leftNode;
      }
      // 右を検索
      else if($p->data < $data)
      {
        $p =& $p->rightNode;
      }
    }
    return false;
  }

  function find($data){
    $p =& $this->root;
    while($p != null){
      if($p->data == $data){
        return true;
      }
      if($p->data > $data){
        $p =& $p->leftNode;
      }else if($p->data < $data){
        $p =& $p->rightNode;
      }
    }
    return false;
  }

  function min(){
    $p =& $this->root;
    if($p == null){
      return -1;
    }
    while($p->leftNode != null){
      $p =& $p->leftNode;
    }
    return $p->data;
  }

  function max(){
    $p =& $this->root;
    if($p == null){
      return -1;
    }
    while($p->rightNode != null){
      $p =& $p->rightNode;
    }
    return $p->data;
  }

}


$n = trim(fgets(STDIN));
$s = trim(fgets(STDIN));
$q = trim(fgets(STDIN));
 
$alpha = "abcdefghijklmnopqrstuvwxyz";
$str = [];
$bt = [];
$shuffle = range(0, $n-1);
shuffle($shuffle);

// 26文字二分探索木を用意
for($i=0;$i<strlen($alpha);$i++){
  $bt[$alpha[$i]] = new BinaryTree();
}

// 初期文字列を格納
for($i=0;$i<strlen($s);$i++){
  $index = $shuffle[$i];
  $bt[$s[$index]]->insert($index+1);
  $str[$index] = $s[$index];
}

for($i=0;$i<count($bt);$i++){
//  echo $alpha[$i]." ".$bt[$alpha[$i]]->min().PHP_EOL;
}

for($i=0;$i<$q;$i++){
  list($p1,$p2,$p3) = explode(" ", trim(fgets(STDIN)));
  if($p1==1){
    // 現在の文字を削除
    $bt[$str[$p2-1]]->delete($p2);
    // 新しい文字を挿入
    $str[$p2-1] = $p3;
    $bt[$p3]->insert($p2);
  }else{
    $count = 0;
    for($j=0;$j<count($bt);$j++){
      $min = $bt[$alpha[$j]]->min();
      $max = $bt[$alpha[$j]]->max();
//      echo $alpha[$j]." ".$min." ".$max." ".$p2." ".$p3.PHP_EOL;      
      if($min != -1 && !($min > $p3 || $max < $p2)){
        $count++;
      }
    }
    echo $count.PHP_EOL;
  }
}