學習博客html
前幾天\(lyk\)給咱們講了倍增可是\(emmmm\)他說話聲音也過小了,坐在後排根本聽不清楚誒,前邊一羣大佬還在嘰嘰喳喳的說話,上課效率低得一批。數組
\[\color{purple}{我太難了}\]學習
顧名思義倍增就是倍增,用\(dsr\)大佬的話來講就是\(wuwu\)膩。優化
咱們有一個數組\(f[i][j]\)表示從\(i\)開始的長度爲\(2^j\)的區間即區間\([i,i+2^j]\)spa
預處理ST表
\(f[i][j]=max(f[i][j-1]),f[i+2^{j-1}][j-1]\)
說句閒話:
爲何這麼寫?我以前第一遍本身學的時候,看其餘人的博客,有的直接不解釋,有的說本身揣摩\(qwq\),我就是揣摩不出來纔去看您的\(blog\)的哇。
後來\(lyk\)講課問誰沒聽懂,偌大的教室裏,只有個人小短手在教室後邊高高的自信的舉起,再不會就對不起\(lyk\)的一對一輔導了.code
靈魂畫師又要入場了
多麼清晰~多麼明瞭~博主已飄htm
for(int i = 1; i <= m; i++) f[i][0] = read();//本身到本身這個區間 for(int j = 1; j <= log(m); j++) for(int i = 1; i + (1 << j) <= m; i++) f[i][j] = min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
j的循環範圍
這裏說一下爲何循環外層是\(j\)
再說一句閒話:
\(wtf\)?剛剛我在畫圖,阿龍過來來了一句:「你又待聶畫畫,打草紙組啥,光待聶畫畫。」嗯??你告訴我往草紙上畫咋粘博客裏。雙面膠?來來來,舞臺留給你,請開始你的表演。因而乎,\(lkx\)大佬幫我畫了張圖
迴歸正題,我們看圖說話並無什麼用的圖假設咱們先枚舉\(i\)那麼咱們的枚舉順序就是\([1,1],[1,2]......[1,m]\)當咱們更新到\([1,2]\)時,就要用到\([1,1]\)和\([2,2]\)這兩個點,可是因爲咱們的外層循環是\(i\)此時的\([2,2]\)尚未值,因此這樣更新出來的就是錯的。blog
把咱們須要查詢的區間\([l,r]\)分紅兩段\([l,2^k]\)到\([r-2^k+1,r]\)其中這個\(k\)是\(2^k \leq r-l+1\)的全部數中的最大的數,就算這兩部分互相覆蓋因爲他們是取最值,因此對答案沒有影響
\(f[l][r]=max(f[l][k],f[r-(1<<k)+1][k])\)
爲何左端點是\(r-2^k+1\)可能只有我這樣的纔會問了很久才明白
簡單一點咱們設\(L\)爲他的左端點,由於是一個閉區間且已知區間長度及區間右端點,可得方程\(r-L+1=2^k\)解得L即爲左端點get
倍增其實還有個很經常使用的應用:博客
此時設\(f[i][j]\)表示從節點\(i\)向上跳\(2^j\)個節點
\(f[i][j]=f[f[i][j-1]][j-1]\)
void work(int x, int fa) { f[x][0] = fa; dep[x] = dep[fa] + 1; for(int i = 1; i < 20; i++) f[x][i] = f[f[x][i - 1]][i - 1]; } void dfs(int x, int fa) { work(x, fa); for(int i = head[x]; i; i = e[i].nxt) if(e[i].to != fa) work(e[i].to, x); }
\(dfs\)的過程當中更新
一種優化常數的方法:先處理出\(lg\)數組,表示以\(2\)爲底\(i\)的對數
for(int i = 2; i <= n; i++) lg[i] = lg[i >> 1] + 1;
先使兩個待查詢的點跳到同一深度,而後再從\(i\)開始枚舉,逼近他們的LCA,最後必定會是在他們的LCA的下一層,最後直接輸出當前節點的爸爸也就是\(f[i][0]\)
if(dep[a] < dep[b]) swap(a, b); for(int i = 20; i >= 0; i--) if(dep[f[a][i]] >= dep[b]) a = f[a][i];//跳到同一深度 if(a == b) return a;//或者是b反正他倆都同樣 for(int i = 20; i >= 0; i--) if(f[a][i] != f[b][i]) a = f[a][i], b = f[b][i];//同時跳 return f[a][0]; }
\(qwq\)貌似是有\(O(1)\)的的查詢的喂
我:「\(lyk\),我\(O(1)\)查詢的不會」
大佬:「\(hai \ xie\),不用會這鍋啊,,誰寫這\(hu\)滴啊」
咱們設\(w[i][j]\)表示從節點\(i\)向上跳\(2^j\)個節點內所通過的最小的權值
for(int j = 1; j < 20; j++) for(int i = 1; i + (1 << j) - 1 <= tot; i++) w[i][j] = min(w[i][j - 1], w[f[i][j - 1]][j - 1]); //f[i][j]是預先處理處的表明i號點向上跳2^j步以後跳到的節點
歷時一上午多點我總算把這篇文章磨完了........
謝謝收看, 祝身體健康!