根号分治
如果有一道题:
一个长度为
n
n
n 的序列
a
a
a,有
m
m
m 次操作,对序列某个元素或者某种意义上的区间进行查询(比如:余数为
p
p
p 的数之和)。
1
≤
n
,
m
≤
1
0
5
1\le n,m\le 10^5
1≤n,m≤105
1
≤
a
i
,
p
≤
1
0
5
1\le a_i,p\le 10^5
1≤ai,p≤105
对于这种操作,直接暴力的复杂度是
O
(
N
)
O(N)
O(N),显然是不可取的。
那我们考虑预处理?用数组存无论的空间还是时间也显然无法操作。
这时候可以考虑一种两者兼备的思路:用数组存空间时间都能接受的部分,剩下的部分暴力。
这就是根号分治。
接下来用一道板子题来详细讲。
例题1
洛谷 - P3396 哈希冲突
先看暴力的做法。
#include<bits/stdc++.h>
#define int long long
#define endl putchar('\n')
using namespace std;
const int N=1e6+5;
int read(){
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar();
return x*f;
}
void print(int x){
if(x<0)putchar('-'),x=-x;
if(x<10){putchar(x+'0');return;}
print(x/10);
putchar(x%10+'0');
}
int n,m;
int a[N];
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i];
while(m--){
char op;
cin>>op;
int x,y;
cin>>x>>y;
if(op=='A'){
int ans=0;
for(int i=y;i<=n;i+=x)ans+=a[i];
print(ans);
endl;
}
else{
a[x]=y;
}
}
}
上面说过,预处理的部分是空间且时间都能接受的部分,那么我们自然是能预处理多少就尽量预处理,均摊后面
m
m
m 次操作的时间。
n
n
n 最大是
1.5
×
1
0
5
1.5\times10^5
1.5×105,
n
log
n
n\log n
nlogn 就显得有些不尊重这个数据了,我们最大可以预处理
O
(
N
N
)
O(N\sqrt N)
O(NN)。
我们用一个数组
m
o
d
mod
mod 存在
n
\sqrt n
n 的范围内的
p
p
p,
m
o
d
i
,
j
mod_{i,j}
modi,j 表示下标对
i
i
i 取余为
j
j
j 的元素之和。那么为什么在
n
\sqrt n
n 以上的模数都不用数组存?因为存不下了,因为对于大于
n
\sqrt n
n 的模数,对应的下标更分散,暴力的时间复杂度在可接受范围内。
#include<bits/stdc++.h>
#define endl putchar('\n')
using namespace std;
const int N=1e6+5;
const int M=1e3+5;
int read(){
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar();
return x*f;
}
void print(int x){
if(x<0)putchar('-'),x=-x;
if(x<10){putchar(x+'0');return;}
print(x/10);
putchar(x%10+'0');
}
int n,m;
int a[N];
int mod[M][M];
signed main(){
cin>>n>>m;
int len=sqrt(n);
for(int i=1;i<=n;i++)a[i]=read();
for(int i=1;i<=len;i++){
for(int j=1;j<=n;j++){
mod[i][j%i]+=a[j];
}
}
while(m--){
char op;
int x,y;
cin>>op>>x>>y;
if(op=='A'){
if(x<=len){
print(mod[x][y]),endl;
}
else{
int res=0;
for(int i=y;i<=n;i+=x)res+=a[i];
print(res),endl;
}
}
else{
for(int i=1;i<=len;i++)mod[i][x%i]-=a[x];
a[x]=y;
for(int i=1;i<=len;i++)mod[i][x%i]+=a[x];
}
}
}
3286

被折叠的 条评论
为什么被折叠?



