[luogu3396] 哈希冲突
题意
思考
很早之前做的这一题,当时觉得这题的根号平衡思想很赞,现在重新回顾一遍,记录下来。
简要题意:给你 \(x,p\) ,从 \(x\) 开始,每隔 \(p\) 个数取一个数,求和
暴力的想法是直接枚举,计算,复杂度 \(O(n^2)\),当然我们也可以对答案进行简单的预处理,令 \(ans[p][i]\) 表示模数是 \(p\),余数是 \(i\)的答案,但预处理复杂度同样是 \(O(n^2)\), 但我们实现 \(O(1)\) 询问了,期望得分 \(10\)
我们得到的两种算法,第一种不用预处理,但询问是 \(O(n^2)\) 的,第二种 \(O(n^2)\) 的预处理,但询问是 \(O(1)\) 的,我们考虑能否将这两种算法中和一下:只预处理 \(p\in[1,\sqrt n]\) 的 \(ans[p][i]\),预处理的复杂度变为了 \(O(n\sqrt n)\),考虑询问,若单次询问的 \(p \leq\sqrt n\),那么它是 \(O(1)\) 的,若 \(p > \sqrt n\),我们暴力统计,易知少于 \(\sqrt n\) 个数对答案有贡献,那么询问的复杂度是 \(O(\sqrt n)\) 的,对于修改操作,我们只需要修改 \(ans[][]\)数组,复杂度\(O(\sqrt n)\), 总体复杂度 \(O((n+m)\sqrt n)\)
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 150050;
int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x * f;
}
int n, m;
int ans[110][110];/// ans[p][i] 对p取模 余数为i
int val[N], MAX, t;
int query(int p, int la){
if(p <= t) return ans[p][la];
int ANS = 0;
for(int i=la; i<=n; i+=p){
ANS += val[i];
}
return ANS;
}
void change(int pos, int v){
for(int j=1; j<=t; j++){
ans[j][pos % j] = ans[j][pos % j] - val[pos] + v;
}
val[pos] = v;
}
int main(){
n = read(); m = read();
t = sqrt(n);
for(int i=1; i<=n; i++) val[i] = read();
for(int i=1; i<=n; i++){
for(int j=1; j<=t; j++){
ans[j][i % j] += val[i];
}
}
while(m --){
char a; cin >> a;
int x, y; cin >> x >> y;
if(a == 'A'){
cout<<query(x, y)<<endl;
}
else change(x, y);
}
return 0;
}
总结
本题的根号平衡思想真的很好,通过对小于 \(\sqrt n\) 和大于 \(\sqrt n\) 的范围进行分治,将复杂度优化成根号