$\newcommand{\O}{\mathrm{O}}$ My Algorithm : kopricky アルゴリズムライブラリ

kopricky アルゴリズムライブラリ

LCA

コードについての説明

木上の任意の $2$ 点間の LCA (lowest common ancestor) を効率的に求めることができるアルゴリズム. また同時に任意の $2$ 点間のキョリも効率的に求めることが可能になる.
実装は $2$ 種類 (euler tour + RMQ か ダブリング) あるが, 以下では euler tour 上での RMQ を用いて求める方法で実装している.
アルゴリズムについては蟻本かこの pdf の説明がわかりやすい.

(関数)
add_edge$(from, to)$ : $from, to$ の間に無向辺を追加する
build$()$ : 加えられた辺の情報を用いてデータ構造を構築する
lca$(u, v)$ : $u, v$ の LCA を求める
dist$(u, v)$ : $u, v$ 間のキョリを求める

時間計算量: 構築 $\O (n)$, 各クエリ $\O (\log n)$

コード

template<typename T> class segtree {
private:
    int n,sz;
    vector<pair<T, int> > node;
public:
    void resize(const vector<T>& v){
        sz = (int)v.size();
        n = 1;
        while(n < sz){
            n *= 2;
        }
        node.resize(2*n);
        for(int i = 0; i < sz; i++){
            node[i+n] = make_pair(v[i], i);
        }
        for(int i=n-1; i>=1; i--){
            node[i] = min(node[2*i], node[2*i+1]);
        }
    }
    pair<T, int> query(int a,int b)
    {
        pair<T, int> res1 = make_pair(numeric_limits<T>::max(), -1);
        pair<T, int> res2 = make_pair(numeric_limits<T>::max(), -1);
        a += n, b += n;
        while(a != b){
            if(a % 2) res1 = min(res1, node[a++]);
            if(b % 2) res2 = min(res2, node[--b]);
            a >>= 1, b >>= 1;
        }
        return min(res1, res2);
    }
};

class LCA{
private:
    int V;
    vector<vector<int> > G;
    vector<int> ord,depth,id;
    segtree<int> st;
    void dfs(int u,int p,int k){
        id[u] = (int)ord.size();
        ord.push_back(u);
        depth[u] = k;
        for(int v : G[u]){
            if(v != p){
                dfs(v,u,k+1);
                ord.push_back(u);
            }
        }
    }

public:
    LCA(int node_size) : V(node_size), G(V), depth(V), id(V, -1){}
    void add_edge(int from,int to){
        G[from].push_back(to),G[to].push_back(from);
    }
    void build(){
        ord.reserve(2*V-2);
        for(int i = 0; i < V; i++){
            if(id[i] < 0){
                dfs(i,-1,0);
            }
        }
        vector<int> stvec(2*V-2);
    	for(int i = 0; i < 2*V-2; i++){
    		stvec[i] = depth[ord[i]];
    	}
        st.resize(stvec);
    }
    int lca(int u,int v){
        return ord[st.query(min(id[u],id[v]),max(id[u],id[v])+1).second];
    }
    int dist(int u,int v){
        int lca_ = lca(u,v);
        return depth[u] + depth[v] - 2*depth[lca_];
    }
};

verify 用の問題

lca の verify
AOJ : Lowest Common Ancestor 提出コード
dist は verify できていないが lca あってたら dist もあってるでしょう...