1. 程式人生 > >板子|LCA倍增解法

板子|LCA倍增解法

目錄

LCA

定義

LCA(Lowest Common Ancestors),即最近公共祖先,是指在有根樹中,找出某兩個結點u和v最近的公共祖先。

重要結論

對於一棵樹:
求兩點x,y的距離:
dist=深度[x]+深度[y]-2*dep[LCA(x,y)];

倍增

解法

1、常規方法有哪些?
2、離線演算法
3、倍增思想
認真分析會發現:
①常規的方式每次向上跳一步太慢了
②每一個狀態的轉移規則都是相同的。
能否預處理出i號節點向上的j輩的祖先呢?(問題:超空間,超時間)
所以能否利用倍增,來加速轉移,縮小空間呢?
——>
類似ST演算法,定義狀態f[i][j],代表從i號節點向上跳2^j步到達的節點。
特殊的,如果跳到“外邊”去了,則f[i][j]=0,f[i][0]
為i的父節點,其他情況:
f[i][j]=f[f[i,j-1],j-1]
按怎樣的順序預處理呢?
——>
基於f陣列,對於x,y的LCA:
1、 BfS出每個節點的深度(假如x在更深的層次),並預處理f陣列
2、 利用倍增將x,y調整到問樣的高度。方法:每次嘗試從x向上走2^k步(k取log2n...3,2,1),檢查到達的節點是不是還是比y深,如果是則令x=f[x][k](思考:
如果走8步還是比y深,下一次為什麼只考慮走4步?)
3、 第二步結束以後可能有兩神情況:
①(說明y是x的某個祖先)
②x,y處在問一層,此時再利用
第二步的方式一起往上跳(You醬普,j醬普)。

板子題目

Problem

板子

P.S. luogu要開氧氣才能過最後3個點,我太弟弟了

#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
#include <cmath>
using namespace std;
void read(int &n){
    int num=0,w=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        num=num*10+ch-'0';
        ch=getchar();
    }
    n=num*w;
}
const int maxn=500005;
int n,m,s;
int dep[maxn],f[maxn][50];
int H;
vector<int> g[maxn];
void init(){
    read(n);read(m);read(s);
    for(int i=1;i<n;i++){
        int u,v;read(u);read(v);
        g[u].push_back(v);
        g[v].push_back(u);
    }
    H=log(n);
}
void bfs(int s){
    queue<int> q;
    q.push(s);dep[s]=1;
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int i=0;i<g[u].size();i++){
            int v=g[u][i];
            if(dep[v]) continue;
            q.push(v);
            dep[v]=dep[u]+1;
            f[v][0]=u;//父親節點
            for(int j=1;j<=H;j++) f[v][j]=f[f[v][j-1]][j-1]; 
        }
    }
}
int LCA(int x,int y){
    if(dep[x]<dep[y]) swap(x,y);
    for(int i=H;i>=0;i--)//將x,y移到同一深度 
        if(dep[f[x][i]]>=dep[y]) x=f[x][i];
    if(x==y) return x;//特判
    for(int i=H;i>=0;i--){
        if(f[x][i]!=f[y][i]){
            x=f[x][i];
            y=f[y][i];
        }
    }
    return f[x][0];
}
void solve(){
    for(int i=1;i<=m;i++){
        int u,v;read(u);read(v);
        printf("%d\n",LCA(u,v));
    }
}
int main(){
    init();
    bfs(s);
    solve();
    return 0;
}