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++14 (GCC 5.4.1) (judge verdict: Accepted)


// E - Simple String Queries

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
// const int INF = 2147483647;
// const ll INF = 9223372036854775807;
// const ll MOD = 1e9 + 7;

// RSQを処理するためのセグメントツリー
class SegmentTreeRSQ {
private:
	vector<ll> nodes;
	ll N; // 葉の数
	ll I = 0; // 単位元

	// 親ノードのインデックスを返す
	ll parent(ll k) {
		return (k - 1) / 2;
	}

	// 左の子のインデックスを返す
	ll left_child(ll k) {
		return (k * 2) + 1;
	}

	// 右の子のインデックスを返す
	ll right_child(ll k) {
		return (k * 2) + 2;
	}

public:
	// 要素数nを処理するセグメントツリーを作成
	// 葉の数はn以上の最小の2冪数、単位元で初期化
	SegmentTreeRSQ(ll n) {
		N = 2;
		while (N < n) N *= 2; // 葉の数を計算
		nodes = vector<ll>(N + (N - 1), I); // 葉以外のノードはN-1個
	}

	// 葉[i]（iは0始まり）にxを加える
	void add(ll i, ll x) {
		ll k = i + N - 1; // 葉[i]のノード番号
		nodes[k] += x;
		while (k > 0) {
			// ノード k の親ノードの番号に変える
			k = parent(k);

			// 左右の子の和を計算しなおす
			nodes[k] = nodes[left_child(k)] + nodes[right_child(k)];
		}
	}

	// [a, b) の区間に対するクエリについて
	// ノード k （区間 [l, r) 担当）が答える
	ll query(ll a, ll b, ll k, ll l, ll r) {
		if (r <= a || b <= l) return I; // 区間が被らない場合は I を返す
		if (a <= l && r <= b) {
			// ノード k の担当範囲がクエリ区間 [a, b)に完全に含まれる
			return nodes[k];
		} else {
			ll c1 = query(a, b, left_child(k), l, (l + r) / 2); // 左の子に値を聞く
			ll c2 = query(a, b, right_child(k), (l + r) / 2, r); // 右の子に値を聞く
			return c1 + c2; // 左右の子の値の和を返す
		}
	}

	// [a, b) の区間に対するクエリについて
	// ノード 0 （全区間担当）が答える
	ll query(ll a, ll b) {
		return query(a, b, 0, 0, N);
	}

	//  for debug
	void print_nodes() {
		int e = 0;
		int d = 1;
		for (ll i=0; i<nodes.size(); i++) {
			cout << nodes[i] << " ";
			if (i == e) {
				cout << endl;
				e += pow(2, d);
				d++;
			}
		}
		cout << endl;
	}
};

int main() {
	int N, Q;
	string S;
	cin >> N >> S >> Q;

	map<char, SegmentTreeRSQ*> st_list;

	for (char ch='a'; ch<='z'; ch++) {
		st_list[ch] = new SegmentTreeRSQ(N);
	}

	for (int i=0; i<S.size(); i++) {
		st_list[S[i]]->add(i, 1);
	}

	char c;
	int t, i, l, r;
	vector<int> ans;
	for (int k=0; k<Q; k++) {
		cin >> t;
		if (t == 1) {
			cin >> i >> c;
			i--;
			for (char ch='a'; ch<='z'; ch++) {
				int t = st_list[ch]->query(i, i+1);
				if (t > 0) st_list[ch]->add(i, -t);
				//cout << "***" << ch << "***" << endl; // **** debug ****
				//st_list[ch]->print_nodes(); // **** debug ****
			}
			st_list[c]->add(i, 1);
		}

		if (t == 2) {
			cin >> l >> r;
			l--;
			int cnt = 0;
			for (char ch='a'; ch<='z'; ch++) {
				if (st_list[ch]->query(l, r) > 0) cnt++;
			}
			ans.push_back(cnt);
		}
	}

	for (int a : ans) {
		cout << a << endl;
	}

	return 0;
}