题意/题解: 给定一个字符串, 1.修改某个字符串中的某个字符, 2.查询字符串某个区间中不同字符的个数。26棵线段树套板子即可。
又是一道直接区修问题,还不太熟悉这类的具体修改,其实还可以加个 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;
}