2025刷题计划--根号算法
简介
对于根号算法,大概有以下几种:
$\quad$1.根号分治
$\quad$2.分块
$\quad$3.莫队
首先对于根号分治,这是一种非常巧妙地算法,他的本质就是将两种非常暴力的算法结合起来。直接说不太好说,下面举一个例子,(引用我看的博客
\(\quad\) \(eg:CF1207F Remainder Problem:\)
\(\quad\)给你一个长度为\(5*10^5\)的序列,初始值为\(0\),完成\(q\)次操作
\(\quad\)\(\quad\)1 X Y:将将下标为\(x\)的位置的值加上\(y\)
\(\quad\)\(\quad\)2 X Y:询问所有下标模\(x\)的结果为\(y\)的位置的值之和。
对于这种题,我们很容易想到一种暴力,就是模拟。他的修改的时间复杂度为\(O(1)\),而他的查询复杂度最差为\(O(nq)\)。
而另一种暴力,则是开一个二维数组\(sum[i][j]\)表示下标模\(i\)的结果为\(j\)。对于修改操作时间为\(O(nq)\),查询复杂度为\(O(1)\),在这个时候,我们发现这两个时间复杂度为两种极端,一种修改复杂度很大,而另一种查询的复杂度很大,这时候如果我们考虑一种阈值,小于阈值的时候我们考虑第二种暴力,大于阈值的时候我们考虑第一种暴力,此时我们设阈值为\(x\),此时时间复杂度为\(O(q(b+\frac{n}{b}))\),而这时,我们利用基本不等式,我们可以得知\(b=\sqrt{n}\)的时候,我们可以算出时间复杂度为\(O(q\sqrt{n})\).时间复杂度正确,可以通过。
例题&练习
T1 CF1806E Tree Master
这道题就是一个比较典的题,对于每一次的询问,首先我们可以发现,当向上找父亲时,到他们的\(Lca\)时,都是加自己的平方,所以我们可以通过进行前缀和优化,而且可以进行记忆化搜索,但是直接进行记忆化搜索空间就开不下,对于一层数量少于\(500\)个,我们可以直接记忆化,而对于大于等于\(500\)个,直接用暴力跳父亲即可
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,q;
const int N=1e5+5;
int a[N],fa[N],sum[N],dep[N],num[N],s[N],f[N][500];
int dfs(int x,int y){
if(x==y)
return sum[x];
if(num[dep[x]]<500){
if(f[x][s[y]])
return f[x][s[y]];
return f[x][s[y]]=dfs(fa[x],fa[y])+a[x]*a[y];
}
else
return dfs(fa[x],fa[y])+1ll*a[x]*a[y];
}
signed main(){
cin>>n>>q;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=2;i<=n;i++)
cin>>fa[i];
fa[1]=0;
for(int i=1;i<=n;i++){
sum[i]=sum[fa[i]]+a[i]*a[i];
dep[i]=dep[fa[i]]+1;
s[i]=++num[dep[i]];
}
while(q--){
int x,y;
cin>>x>>y;
cout<<dfs(x,y)<<"\n";
}
}

浙公网安备 33010602011771号