这道题是比较经典的分块中,单点修改+区间查询的应用
遇见区间问题,我们先想到的是高级数据结构,如树状数组,线段树等,(当然这道题也是一道训练树状数组的好题,只不过编码难度比较高,需要关注空间优化 ,降维),,而分块编码比较简单,需要我们分析复杂度,另外此题的位数查询中有个小trick,少了会TLE.
#include <iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
#define endl '\n'
const int N = 1e5 + 10;
const int B = 320;
int n,m,blen,bnum;
int a[N], bi[N], bl[B], br[B];
int pow10v[12];
int block[B][11][11];//表示第i块中的每个数的第d位是p的总个数
void build(){
blen=sqrt(n);
bnum=(n+blen-1)/blen;
for(int i=1;i<=n;i++){
bi[i] = (i - 1) / blen + 1;
}
for(int i=1;i<=bnum;i++){
bl[i] = (i - 1) * blen + 1;
br[i] = min(n, i * blen);
}
//处理block数组
for(int i=1;i<=bnum;i++){
for(int j=bl[i];j<=br[i];j++){
int cur=a[j];
int num = 1;
while(num<=10){
int low = cur % 10;
block[i][num][low]++;
cur /= 10;
num++;
}
}
}
}
inline int get_digit(int d,int val){
return val / pow10v[d] % 10;
}
void update(int x,int y){
int old_num = a[x];
a[x]=y;
int no=bi[x];
int num = 1;
while(num<=10){
int low=old_num%10;
block[no][num][low]--;
old_num/=10;
num++;
}
num=1;
int cur=y;
while(num<=10){
int low=cur%10;
block[no][num][low]++;
cur/=10;
num++;
}
}
long long query(int l,int r,int d,int p){
long long res=0;
if(bi[l]==bi[r]){
//在同一块
for(int i=l;i<=r;i++){
int cur=a[i];
if(get_digit(d,cur)==p){
res++;
}
}
}else{
for(int i=l;i<=br[bi[l]];i++){
int cur=a[i];
if(get_digit(d,cur)==p)
res++;
}
for (int i = bl[bi[r]]; i <= r; i++)
{
int cur = a[i];
if(get_digit(d,cur)==p)res++;
}
//处理整块
for(int i=bi[l]+1;i<=bi[r]-1;i++){
res += block[i][d][p];
}
}
return res;
}
void solved(){
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i];
build();
char op;
int x,y,l,r,d,p;
for(int i=1;i<=m;i++){
cin>>op;
if(op=='S'){
cin>>x>>y;
update(x, y);
}else{
cin>>l>>r>>d>>p;
cout<<query(l,r,d,p)<<endl;
}
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);cout.tie(nullptr);
int T=1;
cin>>T;
pow10v[1] = 1;
for (int i = 2;i<=10;i++){
pow10v[i] = pow10v[i - 1] * 10;
}
while (T--)
{
memset(block, 0, sizeof(block));
solved();
}
return 0;
}

1264

被折叠的 条评论
为什么被折叠?



