题意:给出一个序列 然后操作是询问在某一个区间中某一位上的某一个数字的个数
或者是修改其中的某一个数字
解法:这题一上来自然就敲了一个线段树 然而空间要4000w 不用交就知道mle啊
然后想到树状数组那也就1000w 但是这个也是太大了
有另外一种做法 就是拿时间来换空间 离线所有询问 然后对于每一位的询问分别进行询问 那么询问就变成100w了
这个线段树估计是被卡常数了 然后树状数组把挂全部加上 rp好的话或许卡过
这里采用的做法是进行标记 就像刚开始写大数假发一样 进行标记 考虑到unsigned short可以在空间上满足1000w的要求 再加上一个bool标记 基本上就可以在空间限制范围内了 其余的应该都会吧
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int _,t,n,m,a[100010],x,y;
char op[5];
unsigned short sum[100001][10][10];
bool fl[100001][10][10];
int scan(){
int res;char ch;bool neg;
while(ch=getchar(),!isdigit(ch)&&ch!='-');
if(ch=='-'){
res=0;
neg=true;
}else{
res = ch - '0';
neg = false;
}
while (ch = getchar(), isdigit(ch))
res=res*10+ch-'0';
return neg ? -res : res;
}
inline void add(int q,int w,int x,int y){
for(;x<=n;x+=x&-x){
if(y==1){
if(sum[x][q][w]==65535)sum[x][q][w]=0,fl[x][q][w]=1;
else sum[x][q][w]++;
}else{
if(sum[x][q][w]==0)sum[q][w][x]=65535,fl[x][q][w]=0;
else sum[x][q][w]--;
}
}
}
inline int _sum(int q,int w,int x){
int t=0;
for(;x;x-=x&-x)t+=sum[x][q][w]+(fl[x][q][w]?65536:0);
return t;
}
int main(){
scanf("%d",&_);
while(_--){
n=scan();m=scan();
for(int i=0;i<10;++i)
for(int j=0;j<10;++j)
for(int k=1;k<=n;++k)
sum[k][i][j]=fl[k][i][j]=0;
for(int i=1;i<=n;i++){
a[i]=scan();
for(int t=a[i],j=0;j<10;t/=10,j++)add(j,t%10,i,1);
}
while(m--){
scanf("%s",op);
if(op[0]=='S'){
x=scan();y=scan();
for(int t=a[x],j=0;j<10;t/=10,j++)
add(j,t%10,x,-1);
a[x]=y;
for(int t=a[x],j=0;j<10;t/=10,j++)
add(j,t%10,x,1);
}else{
x=scan();y=scan();int i=scan();int j=scan();i--;
int __sum=_sum(i,j,y)-_sum(i,j,x-1);
printf("%d\n",__sum);
}
}
}
return 0;
}