洛谷P3396哈希冲突
题意:
给出长度为n的序列A1、A2、A3……An,进行M次操作:‘A’操作输入x、y,求序列中下标i%x==y的所有数的和;‘C’操作输入x、y,将Ax的值改为y。
题解:
题解真的非常神奇……思路是这样的:
首先暴力的想法:
for(int i=y;i<=n;i+=x) ans+=A[i];
这样是O(n^2)的。之后有O(1)求答案的预处理,预处理出每个数在下标%p时对答案的贡献:
void Init(){
for(int i=1;i<=n;i++){
for(int p=1;p<n;p++){
ans[p][i%p]+=a[i];
}
}
return;
}
然而,这个预处理的时间和空间复杂度都达到了O(n^2)……不过我们可以利用根号来进行优化:
void Init(){
int size=sqrt(n);
for(int i=1;i<=n;i++){
for(int p=1;p<=size;p++){
ans[p][i%p]+=a[i];
}
}
return;
}
这样时间和空间的复杂度降到了O(n*sqrt(n))。
预处理后,对于询问中模数小于等于sqrt(n)的,我们都可以O(1)解决;而对于我们没有处理出的,即模数大于sqrt(n)的,我们知道序列里对答案有贡献的数不会超过sqrt(n)个,所以暴力求和,时间复杂度维持在O(sqrt(n))以下。
对于修改,暴力更新预处理的结果即可,复杂度维持在O(sqrt(n))以下。
所以总复杂度为O(n*sqrt(n))。
只能说根号还是很神奇的……
代码:
#include<cstdio>
#include<cmath>
#define MXN 150000+1
#define MXM 150000+1
int n,m;
int a[MXN],ans[400][400];
char cmd[2];
int x,y,size;
void Init(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
size=sqrt(n);
for(int i=1;i<=n;i++){
for(int j=1;j<=size;j++){
ans[j][i%j]+=a[i];
}
}
return;
}
void Query(){
scanf("%d%d",&x,&y);
if(x<=size) printf("%d\n",ans[x][y]);
else{
int sum=0;
for(int i=y;i<=n;i+=x) sum+=a[i];
printf("%d\n",sum);
}
return;
}
void Change(){
scanf("%d%d",&x,&y);
for(int i=1;i<=size;i++){
ans[i][x%i]=ans[i][x%i]-a[x]+y;
}
a[x]=y;
return;
}
int main(){
Init();
while(m--){
scanf("%s",cmd);
if(cmd[0]=='A') Query();
if(cmd[0]=='C') Change();
}
return 0;
}