倍增
有的时候我们一步一步算很慢,比如求 \(lca(u,v)\) 的时候。所以我们需要加速。
众所周知,任意一个整数一定可以拆分为类似 \(2^{k_1}+2^{k_2}+2^{k_3}...2^{k_n}\) 之和。换而言之,假设我们操作 \(x\) 步,我们完全可以拆分成 \(2^{k_1}+2^{k_2}+2^{k_3}...\) 的形式,预处理出来,然后用这些步数来跳,就可以从 \(O(n)\) 优化到 \(O(\log _2n)\) 。
要求:满足结合律,前后运算规则相同。否则我们无法合并。
预处理:\(O(n\log_2n)\),查询 \(O(\log_2 n) ~or~ O(1)\)。
example
下面给出最常见的两种倍增算法。
LCA
定义 \(f_{i,j}\) 表示从 \(i\) 开始,往上跳 \(2^{j}\) 步。显然 \(f_{i,j}=f_{f_{i,j-1},{j-1}}\)。
inline void dfs(int u,int v){
f[u][0]=v;dep[u]=dep[v]+1;
for(int i=1;i<M;i++) f[u][i]=f[f[u][i-1]][i-1];
for(int i=head[u];i;i=nxt[i]) if(to[i]!=v) dfs(to[i],u);
}
inline int Lca(int u,int v){
if(dep[u]<dep[v]) swap(u,v);
for(int i=M-1;i>=0;i--) if(dep[f[u][i]]>=dep[v]) u=f[u][i];
if(u==v) return u;
for(int i=M-1;i>=0;i--) if(f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i];
return f[u][0];
}
这份代码记得注意 \(dep_{rt}\) 要为 \(1\),否则跳的时候会和 \(0\) 混为一谈。
ST 表
定义 \(f_{i,j}\) 表示第 \([i,i+2^j-1]\) 的需要合并信息。
显然有 $Merge(f_{i,j-1},f_{i+{2^{j-1},j-1}})\to f_{i,j} $ 。
inline int Query(int l,int r){
int k=log2(r-l+1);
return max(f[l][k],f[r-(1<<k)+1][k]);
}
inline void init(int n){
F(i,1,n) f[i][0]=read();
F(j,1,24) {
for(int i=1; i+(1<<j)-1<=n; i++) {
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
}
return;
}

浙公网安备 33010602011771号