题目背景
此题约为NOIP提高组Day2T2难度。
题目描述
众所周知,模数的hash会产生冲突。例如,如果模的数p=7,那么4和11便冲突了。
B君对hash冲突很感兴趣。他会给出一个正整数序列value[]。
自然,B君会把这些数据存进hash池。第value[k]会被存进(k%p)这个池。这样就能造成很多冲突。
B君会给定许多个p和x,询问在模p时,x这个池内**数的总和**。
另外,B君会随时更改value[k]。每次更改立即生效。
保证1<=p<n1<=p<n.
输入格式
第一行,两个正整数n,m,其中n代表序列长度,m代表B君的操作次数。
第一行,n个正整数,代表初始序列。
接下来m行,首先是一个字符cmd,然后是两个整数x,y。
-
若
cmd='A',则询问在模x时,y池内数的总和。 -
若
cmd='C',则将value[x]修改为y。
输出格式
对于每个询问输出一个正整数,进行回答。
Solution
神奇的解法:根号算法——不只是分块
设ans[x][y]表示膜x余y的答案(先别急着说数据范围太大)
我们发现数组记忆的方法预处理复杂度为O(n^2),操作复杂度为O(n);
而暴力法无预处理,操作复杂度为O(n^2);
考虑到数组大小的问题,我们要改进数组记忆法
发现x越大时询问操作复杂度越低,当x>根号n时复杂度<根号n
于是有了新的结合版算法:预处理x<=根号n的ans数组,询问时x<=根号n,x>根号n暴力
整个复杂度O((n+m)* 根号n);
Code
#include <cstdio> #include <cstdlib> #include <cmath> using namespace std; const int N=15e4+10; int ans[1000][1000],n,m,d[N],x,y,p; char s[5]; int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&d[i]); p=sqrt(n); for(int i=1;i<=p;i++) for(int j=1;j<=n;j++) ans[i][j%i]+=d[j]; while(m--) { scanf("%s%d%d",s,&x,&y); if(s[0]=='A') { if(x<=p) { printf("%d\n",ans[x][y]); continue; } int an=0; for(int i=y;i<=n;i+=x) an+=d[i]; printf("%d\n",an); } else { for(int i=1;i<=p;i++) ans[i][x%i]+=y-d[x]; d[x]=y; } } return 0; }