倍增

BSS梅者如西發表於2024-10-24

RMQ問題

我們用dp[i][j]表示[i,i+2^j-1]區間的最大值,預處理的時候可以分兩個部分進行查詢最大值。
image
查詢區間[l,r]的最大值時我們只用查詢(l,l+2^s-1 )與(r-2^s+1,r),其中s=log2(r-l+1)就可以了,我們保證(l,r)的區間一定能覆蓋到且不會超出。
預處理時間複雜度O(nlogn),查詢時間複雜度O(1)

點選檢視程式碼
int a[N],dp[N],n;
void prep(){//預處理
    for(int i=1;i<=n;i++){
        cin>>a[i];
        dp[i][0]=a[i];
    }
    for(int j=1;(j>>1)<=n;j++){
        for(int i=1;i<=n;i++){
            dp[i][j]=max(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
        }
    }
}
int query(int l,int r){//查詢
    int k=log2(r-l+1);
    return max(dp[l][k],dp[r-(1<<k)+1][k]);
}

LCA

最近公共祖先簡稱 LCA(Lowest Common Ancestor)。兩個節點的最近公共祖先,就是這兩個點的公共祖先裡面,離根最遠的那個。
本題考慮倍增,用fa[x][i]表示節點x的第2^i個祖先,先使兩個節點到達同一深度,然後再同步從上往下跳。

點選檢視程式碼
int vis[N],dis[N],f[N][20];
vector<int>g[N];
void dfs(int x,int d){
    vis[x]=1;
    dis[x]=d;
    for(int i=1;(1<<i)<=k;i++){
        f[x][i]=f[f[x][i-1]][i-1];
    }
    for(int v:g[x]){
        if(v==fa[x][0]) continue;
        fa[v][0]=x;
        dfs(v,d+1);
    }
}
int lca(int x,int y){
    if(dis[y]>dis[x]) swap(x,y);
    int k=dis[x]-dis[y];
    for(int i=1;(1<<i)<=k;i++){//對於xy距離k進行二進位制劃分
        if(k&(1<<i)) x=fa[x][i];
    }
    if(x==y) return x;
    for(int i=19;i>=0;i--){//從上往下跳,最後一定跳到LCA的下一層
        if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    }
    return fa[x][0];
}

相關文章