POJ 1986 Distance Queries

http://poj.org/problem?id=1986 http://bailian.openjudge.cn/practice/1986?lang=en_US

https://vjudge.net/problem/POJ-1986 https://vjudge.net/problem/OpenJ_Bailian-1986

POJだとMLがきついのでOpenJ_BailianでAC。

問題概要

POJ 1984 Navigation Nightmareと同じ入力形式の問題なのでこちらの問題文を読む必要もある )

  • 2次元平面状に$N$個の農場があり、$1\cdots N$の番号が付いている

  • 長さの異なる$M$本の道路があり、$i$本目の道路は$A_i$番目の農場から$B_i$番目の農場へ続く距離$L_i$の道路で、$D_i\in{N,E,W,S}$方向につながっている。

  • 2つの農場の間にはちょうど1つパスが存在する。(木になっている)

  • 面倒な入力は与えられない。

    • 農場は道路の端点にのみある
    • 道路が交差することはない
    • 矛盾するデータが来ることはない
  • $K$個のクエリが与えられるので答えよ。

  • クエリ $a_i,b_i$ : 農場$a_i$から農場$b_i$に移動するとき通る道路の長さの合計を求めよ

制約

  • $2\leq N\leq 4\times10^4$
  • $1\leq M< 4\times10^4$
  • $1\leq L_i\leq 10^3$
  • $1\leq K\leq 10^4$

解法メモ

  • 木の適当な頂点を根とする。
  • 根から他の頂点$v$への距離$d(v)$を列挙しておけば、答えは$d(a_i)+d(b_i)-2d(\mathrm{lca}(a_i,b_i))$となる。
  • 方位が与えられるがこの問題では特に関係ない

実装例

POJだとMLが30000 kBなのでMLE

#include <algorithm>
#include <cassert>
#include <cstdio>
#include <iostream>
#include <stack>
#include <vector>
#define rep(i, n) for (int i = 0, i##_len = (n); i < i##_len; ++i)
#define inf int(2e9)
using namespace std;
#define all(v) v.begin(), v.end()

template <typename edge>
struct LCA {
   private:
    class SparseTable {
        vector<vector<unsigned long long> > st;
        vector<int> lookup;
        int n, N, logN;

       public:
        SparseTable() {}
        SparseTable(const vector<unsigned long long> &v) {
            n = v.size();
            if (n == 0) return;
            N = 1 << (32 - __builtin_clz(v.size()));
            logN = 32 - __builtin_clz(N);
            st.assign(logN, vector<unsigned long long>(N, (unsigned long long)-1));
            for (int i = 0; i < n; ++i) st[0][i] = v[i];
            int len = 1;
            for (int i = 1; i < (int)st.size(); ++i) {
                for (int j = 0; j + len < N; j++) {
                    st[i][j] = min(st[i - 1][j], st[i - 1][j + len]);
                }
                len <<= 1;
            }
            lookup.resize(N + 1);
            for (int i = 2; i < (int)lookup.size(); ++i) lookup[i] = lookup[i >> 1] + 1;
        }

        unsigned long long prod(int l, int r) {
            assert(0 <= l && l <= r && r <= n);
            if (l == r) return (unsigned long long)-1;
            int b = lookup[r - l];
            return min(st[b][l], st[b][r - (1 << b)]);
        }
    };
    const vector<vector<edge> > &G;
    SparseTable st;

    void dfs() {
        stack<int, vector<int> > s;
        s.push(root);
        while (!s.empty()) {
            int v = s.top();
            s.pop();
            if (v >= 0) {
                _id[v] = _vs.size();
                _vs.push_back(v);
                for (int i = 0; i < int(G[v].size()); ++i) {
                    const edge &e = G[v][i];
                    if (_parent[v] == e.to) continue;
                    _parent[e.to] = v;
                    _depth[e.to] = _depth[v] + 1;
                    s.push(~v);
                    s.push(e.to);
                }
            } else {
                _vs.push_back(~v);
            }
        }
    }

   public:
    int root, logV;
    vector<int> _parent, _depth, _vs, _id;
    LCA(const vector<vector<edge> > &_G, int _root = 0) : G(_G), root(_root) {
        logV = 1;
        int _v = 1;
        while (_v < int(G.size())) _v *= 2, logV++;
        _parent = vector<int>(G.size());
        _depth = vector<int>(G.size());
        _id = vector<int>(G.size());
        dfs();
        vector<unsigned long long> v(_vs.size());
        for (int i = 0; i < int(_vs.size()); ++i) v[i] = ((unsigned long long)_depth[_vs[i]] << 32) | _vs[i];
        st = SparseTable(v);
    }

    int getLca(int u, int v) { return st.prod(min(_id[u], _id[v]), max(_id[u], _id[v]) + 1) & ((1ull << 32) - 1); }
    int getDist(int u, int v) { return _depth[u] + _depth[v] - 2 * _depth[getLca(u, v)]; }
};

struct edge {
    int to;
    int cost;
    edge(int _to, int _cost) : to(_to), cost(_cost) {}
};
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    int n, m;
    cin >> n >> m;
    vector<vector<edge> > G(n);

    rep(i, m) {
        int a, b, c;
        char d;
        cin >> a >> b >> c >> d;
        a--, b--;
        G[a].push_back(edge(b, c));
        G[b].push_back(edge(a, c));
    }
    vector<int> dist(n, inf);
    dist[0] = 0;
    stack<int, vector<int> > st;
    st.push(0);
    while (!st.empty()) {
        int u = st.top();
        st.pop();
        rep(i, G[u].size()) {
            edge e = G[u][i];
            if (dist[e.to] > dist[u] + e.cost) {
                dist[e.to] = dist[u] + e.cost;
                st.push(e.to);
            }
        }
    }

    LCA<edge> lca(G);
    int q;
    cin >> q;
    rep(i, q) {
        int a, b;
        cin >> a >> b;
        a--, b--;
        cout << dist[a] + dist[b] - 2 * dist[lca.getLca(a, b)] << endl;
    }
}