P3396 哈希冲突
像这样的题大多可以考虑根号分治,也就是和模数有关的。
我们对于询问的值来分治:
对于小于 \(\sqrt{n}\) 的模数:
说明剩余系也不超过 \(\sqrt{n}\) 个,于是我们可以预处理这样的询问,需要时直接回答即可。
预处理时间复杂度是 \(O(n\sqrt{n})\) 的,询问 \(O(1)\)
对于大于等于 \(\sqrt{n}\) 的模数:
每个剩余系里面的元素不超过 \(\sqrt{n}\) 个。
于是对于每一个询问,我们可以直接枚举起点,然后不断地累加这个剩余系里面下标的答案即可。
这部分询问是 \(O(\sqrt{n})\) 的,时间复杂度是 \(O(m\sqrt{n})\) 。
代码:
#include<bits/stdc++.h>
using namespace std;
template <typename T>
inline void read(T &x){
x=0;bool f=false;char ch=getchar();
while(!isdigit(ch)){if(ch=='-'){f=true;}ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
x=f?-x:x;
return ;
}
template <typename T>
inline void write(T x){
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10^48);
return ;
}
const int N=1e5+5e4+5,M=405,INF=1e9+7;
int n,m,t,a[N];
int sum[M][M];//摸i时第j个池子的情况
int main(){
read(n),read(m);t=sqrt(n);
for(int i=1;i<=n;i++) read(a[i]);
for(int i=1;i<=t;i++) for(int j=1;j<=n;j++) sum[i][j%i]+=a[j];
for(int i=1,x,y;i<=m;i++){
char op[5];
scanf("%s",op);read(x),read(y);
if(op[0]=='A'){
if(x<=t) write(sum[x][y]),putchar('\n');
else{
int Sum=0;
for(int j=y;j<=n;j+=x) Sum+=a[j];
write(Sum),putchar('\n');
}
}
else{
for(int j=1;j<=t;j++) sum[j][x%j]-=a[x];
for(int j=1;j<=t;j++) sum[j][x%j]+=y;
a[x]=y;
}
}
return 0;
}