简要题意
你需要维护一个长度为 \(n\) 的序列 \(v\),支持:
A x y
求整个序列中,所有模 \(x\) 为 \(y\) 的下标的元素的值,即:
C x y
,将 \(v_x\) 修改为 \(y\)。
思路
根号分治是一种玄学的暴力优化,如果一道题的暴力有很多种写法,且每一种写法有自己独特的优势(如大值较快,小值较快等),则可以考虑根号分治。
比如说这道题,有两种方法:
- 直接暴力找序列中模 \(x\) 为 \(y\) 的所有下标,并把它们加起来。这样子大的 \(x\) 跑得快,小的值跑得慢。
- 预处理,小 \(x\) 较快,大的 TLE/MLE。
我们可以议定一个界 \(R\),使得 \(x>R\) 的选择暴力,小于等于 \(R\) 的预处理。这里,\(R=\sqrt{n}\) 时较优。
总结:根号分治就是选择暴力方法,扬长避短,用暴力乱踩正解!
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n,m;
int a[1500005],f[500][500];
int bl;
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
bl=sqrt(n);
for(int i=1;i<=n;i++){
for(int j=1;j<=bl;j++){
f[j][i%j]+=a[i];
}
}
while(m--){
char op;int x,y;
cin>>op>>x>>y;
if(op=='A'){
if(x<=bl){
cout<<f[x][y]<<'\n';
}
else{
int ans=0;
for(int i=y;i<=n;i+=x){
ans+=a[i];
}
cout<<ans<<'\n';
}
}
else{
for(int i=1;i<=bl;i++){
f[i][x%i]+=(y-a[x]);
}
a[x]=y;
}
}
return 0;
}
标签:P3396,int,分治,哈希,序列,根号,暴力
From: https://www.cnblogs.com/zheyuanxie/p/p3396.html