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: Time Limit Exceeded)

<?php
class Set {
	protected $list = [];
	protected $sortList = [];
	protected $isDirty = false;
	
	// 値追加
	public function add(int $x) {
		$this->list[$x] = $x;
		$this->isDirty = true;
	}
	
	// 値削除
	public function remove(int $x) {
		unset($this->list[$x]);
		$this->isDirty = true;
	}
	
	// 見つける
	public function lowerBound(int $x) {
		$cnt = count($this->list);
		
		// データがなければfalseを返す
		if($cnt <= 0) {
			return(false);
		}
		// ソートを掛ける
		if($this->isDirty !== false) {
			$this->sortList = array_keys($this->list);
			sort($this->sortList);
			$this->isDirty = false;
		}
		
		// 端をチェック
		if($this->sortList[$cnt - 1] < $x) {
			return(false);
		}
		
		// 低い方で探す
		$sIdx = 0;
		$eIdx = count($this->sortList) - 1;
		
		$flg = false;
		while($sIdx <= $eIdx) {
			// 真ん中
			$idx = intval(($eIdx + $sIdx) / 2);
			
			if($this->sortList[$idx] == $x) {
				return($this->sortList[$idx]);
			}
			if($this->sortList[$idx] > $x) {
				$eIdx = $idx - 1;
			}
			else {
				$sIdx = $idx + 1;
				$idx++;
			}
		}
		
		if($idx >= $cnt) {
			return(false);
		}
		
		return($this->sortList[$idx]);

	}
}

fscanf(STDIN, "%d", $N);
fscanf(STDIN, "%s", $S);
fscanf(STDIN, "%d", $Q);

$rangeList = range('a', 'z');

// 各文字が何文字目にあるかを管理
$mojiIdx = [];
foreach($rangeList as $v) {
	$mojiIdx[$v] = new Set();
}

for($i = 0; $i < $N; $i++) {
	$mojiIdx[$S[$i]]->add($i);
}

$ansList = [];
for($i = 0; $i < $Q; $i++) {
	fscanf(STDIN, "%d %d %s", $a, $b, $c);
	
	$b--;
	if($a == 1) {
		$mojiIdx[$S[$b]]->remove($b);
		$S[$b] = $c;
		$mojiIdx[$S[$b]]->add($b);
	}
	else {
		$c = intval($c) - 1;
		$ans = 0;
		foreach($rangeList as $v) {
			$wk = $mojiIdx[$v]->lowerBound($b);
/*
			var_dump($mojiIdx[$v]);
			echo "{$wk}: {$v}: {$b}\n";
*/
			if($wk !== false && $wk <= $c) {
				$ans++;
			}
		}
		$ansList[] = $ans;
	}
}

foreach($ansList as $v) {
	echo "{$v}\n";
}