關於倍增

關於倍增

學習博客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

倍增其實還有個很經常使用的應用:博客

求LCA!

此時設\(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步以後跳到的節點

歷時一上午多點我總算把這篇文章磨完了........

謝謝收看, 祝身體健康!

相關文章
相關標籤/搜索