题意
思考
很早之前做的这一题,当时觉得这题的根号平衡思想很赞,现在重新回顾一遍,记录下来。
简要题意:给你 (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)) 的,我们考虑能否将这两种算法中和一下:只预处理 (pin[1,sqrt n]) 的 (ans[p][i]),预处理的复杂度变为了 (O(nsqrt n)),考虑询问,若单次询问的 (p leqsqrt 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) 的范围进行分治,将复杂度优化成根号