倍增入門
倍增
線性倍增
預處理
\(f[i][j]\) 表示從\(i\) 開始的 長度為\(2^{j}\) 的區間(即區間\([i, i+2^{j}-1]\) )
遞推公式(j在外層遞增):
\(f[i][j]=max\{f[i][j-1], f[i+2^{j-1}][j-1]\}\)
即將區間\([l, r]\) 分為兩個區間合併
查詢
分為兩段,第一段為區間\([l, k]\) ,第二段為區間\([r-k+1, r]\) ,其中\(k\) 為滿足\(2^{k}\le r-l+1\) 的所有數中最大的那個數
\(f[i][j]=max\{f[i][k], f[j-2^{k}+1][j]\}\)
樹上倍增(求解LCA)
預處理
首先結合dfs預處理出\(f[i][j]\) ,\(f[i][j]\) 表示節點\(i\) 向上跳\(2^{j}\) 層的節點
遞推公式:
\(f[i][j]=f[f[i][j-1]][j-1]\)
即節點\(i\) 分兩次向上跳,每次跳\(2^{j-1}\) 層跳到的節點就是節點\(i\) 向上跳\(2^{j}\) 層的節點(\(2^{j-1}\times 2=2^{j}\) )
void dfs(int x, int fa){ dep[x]=dep[fa]+1; f[x][0]=fa; for(int j=1;(1<<j)<=dep[x];j++) f[x][j]=f[f[x][j-1]][j-1]; for(int i=0;i<mp[x].size();i++) if(mp[x][i]!=fa) dfs(mp[x][i], x); }
同時也可以順便預處理出\(log^{n}_{2}\) 的所有值以優化常數
查詢
首先使兩個查詢節點跳至同一高度後(因為它們的最近公共祖先不可能低於這兩點,跳躍方法同下),當前層記為\(x\) ,然後從\(log^{x}_{2}\) 到0列舉(遞減能保證可以完全分解成二進位制)\(j\) ,如果上跳\(2^{j}\) 層後不重合,那麼就繼續跳,重合則不跳,使兩點層數一直逼近最近公共祖先,最後跳完\(2^{0}\) 層後,兩點必定會停在最近公共祖先的下一層,所以最後直接取當前層\(i\) 的\(f[i][0]\) 就好了。
其中,每次取得k的while((1<<t)<=dep[a])
迴圈可以如前文提到的那樣先打表出\(log^{n}_{2}\)
的所有值以優化常數
int lca(int a, int b){ if(dep[a]<dep[b]) swap(a,b); int t=0; if(dep[a]!=dep[b]){ while((1<<t)<=dep[a]) t++;t-=1; for(int i=t;i>=0;i--){ if(dep[a]-(1<<i)>=dep[b]) a=f[a][i]; } } if(a==b)return a; t=0; while((1<<t)<=dep[a]) t++;t-=1; for(int i=t;i>=0;i--) if(f[a][i]!=f[b][i]) a=f[a][i], b=f[b][i]; return f[a][0]; }