【题解】简单字符串变换 (2020 NOIP模拟)
问题描述
给你一个长度为n且只包含小写字母的字符串S(下标从1开始到n),执行Q次下面的两种操作:
1 i c:将字符串S中第i个位置的字符替换成字符c;
2 l r:查询字符串S中从第l个到第r个位置之间不同字符的个数。输入格式
第一行 包含一个整数n,表示字符串S的长度。
第二行 包含一个长度为n的字符串S,只包含小写字母。
第三行 包含一个整数Q,表示操作的个数。
第4~Q+3行,每行描述上述两种操作中的一种。输出格式
对于每个操作2输出一行包含一个整数,表示S[l...r]中不同字符的个数。样例
输入
7
abcdbbd
6
2 3 6
1 5 z
2 1 1
1 4 a
1 7 d
2 1 7输出
3
1
5数据范围
50%的数据:1≤n≤100
100%的数据:1≤n≤500000,1≤Q≤20000
题解
这道题很显然可以用树状数组做(然而我并不会写,但是鉴于分块简单而好想的思路(甚至分块跑得比树状数组还快一点?),我在这里讲一下分块的做法。
把S分割成sqrt(n)段区间,每次修改时只修改第i个位置所属区间的相应字母个数,统计答案时再依次将每个区间都加起来。总之就是分块模板
代码
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<bits/stdc++.h>
using namespace std;
const int N=500005;
int n,q;
int block,num,belong[N],l[750],r[750];
int a[750][26];
char s[N];
void work1(){
int k;char c[2];
scanf ("%d%s",&k,c);
a[belong[k]][s[k]-'a']--;
a[belong[k]][c[0]-'a']++;
s[k]=c[0];
}
void work2(){
int x,y;
int d[26]={0};
scanf ("%d%d",&x,&y);
for (int i=x;i<=min(r[belong[x]],y);i++) d[s[i]-'a']++;
if (belong[x]!=belong[y])
for (int i=l[belong[y]];i<=y;i++) d[s[i]-'a']++;
for (int i=belong[x]+1;i<=belong[y]-1;i++){
for (int j=0;j<26;j++)
d[j]+=a[i][j];
}
int sum=0;
for (int j=0;j<26;j++) if (d[j]) sum++;
printf ("%d\n",sum);
}
int main(){
//freopen("string.in","r",stdin);
//freopen("string.out","w",stdout);
scanf ("%d%s",&n,s+1);
block=int (sqrt(n));
num=n/block;
if (n%block) num++;
for (int i=1;i<=n;i++)
belong[i]=(i-1)/block+1;
for (int i=1;i<=num;i++){
l[i]=(i-1)*block+1;r[i]=i*block;
for (int j=l[i];j<=min(n,r[i]);j++)
a[i][s[j]-'a']++;
}
r[num]=n;
scanf ("%d",&q);
while (q--){
int tmp;
scanf ("%d",&tmp);
if (tmp==1) work1();
else work2();
}
return 0;
}

浙公网安备 33010602011771号