Live2D
返回顶部

【题解】简单字符串变换 (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;
}
posted @ 2020-08-13 16:22  Sporadic_memory  阅读(653)  评论(1)    收藏  举报