题意/题解: 给定一个字符串, 1. 1. 1.修改某个字符串中的某个字符, 2. 2. 2.查询字符串某个区间中不同字符的个数。26棵线段树套板子即可。

又是一道直接区修问题,还不太熟悉这类的具体修改,其实还可以加个 l a z y lazy lazy标记,不过查询并不多直接做就好了,由于每次修改只涉及两个字符所以只需要修这两棵线段树即可。


#include<bits/stdc++.h>
using namespace std;

const int N = 5e5 + 10;
char s[N];
int n, q;

struct Node{
	int l, r;
	int v;
}t[26][N << 2];

void pushup(Node tr[], int u) {
	tr[u].v = tr[u << 1].v + tr[u << 1 | 1].v;
}

void build(int i, Node tr[], int u, int l, int r) {
	tr[u] = {l, r, 0};
	if(l == r) {
		tr[u].v = (s[l] - 'a' == i);
		return ;
	} 
	
	int mid = l + r >> 1;
	build(i, tr, u << 1, l, mid), build(i, tr, u << 1 | 1, mid + 1, r);
	pushup(tr, u);
}

void modify(int i, Node tr[], int u, int x, int d) {
	if(tr[u].l == x && tr[u].r == x) {
		tr[u].v = d;
		return ;
	}
	
	int mid = tr[u].l + tr[u].r >> 1;
	if(x <= mid) modify(i, tr, u << 1, x, d);
	else modify(i, tr, u << 1 | 1, x, d);
	pushup(tr, u);
} 

int query(int i, Node tr[], int u, int l, int r) {
	if(l <= tr[u].l && r >= tr[u].r) return tr[u].v;
	
	int mid = tr[u].l + tr[u].r >> 1;
	
	int res = 0;
	if(l <= mid) res += query(i, tr, u << 1, l, r);
	if(r > mid) res += query(i, tr, u << 1 | 1, l, r);
	
	return res;
}

int main()
{
	scanf("%d%s%d", &n, s + 1, &q);
	for(int i = 0; i < 26; i++) build(i, t[i], 1, 1, n);
	
	while(q--) {
		int op, x, y; char ch[2];
		scanf("%d", &op);
		
		if(op == 1) {
			scanf("%d%s", &x, ch);
			char c2 = s[x];
			if(c2 == ch[0]) continue;
			else {
				s[x] = ch[0];
				int t1 = ch[0] - 'a';
				int t2 = c2 - 'a';
				modify(t1, t[t1], 1, x, 1);
				modify(t2, t[t2], 1, x, 0);
			}	
		}
		
		else {
			scanf("%d%d", &x, &y);
			int ans = 0;
			for(int i = 0; i < 26; i++) if(query(i, t[i], 1, x, y)) ans++;
			printf("%d\n", ans);
		}
	}
	
	return 0;
}