1 条题解

  • 0
    @ 2025-5-17 9:02:31
    #include <bits/stdc++.h>
    using namespace std;
    // 原图
    int F, M, N;
    int R;
    int dx[] = {0, 0, 0, 0, 1, -1};
    int dy[] = {0, 0, 1, -1, 0, 0};
    int dz[] = {1, -1, 0, 0, 0, 0};
    // 获取第 p 层第 q 行第 r 列的编号
    int getId(int p, int q, int r)
    {
        return (p - 1) * (M * N) + (q - 1) * M + r;
    }
    // 新图
    const int MAXN = 200000 * 2;
    vector<int> e[MAXN + 5];
    // 标黑色恐怖距离
    queue<int> q;
    int w[MAXN + 5]; // 原图每个点的黑色恐怖距离
    // 封装 LCA
    namespace LCA
    {
        int n, m, s;
        vector<int> e[MAXN + 5];
        // f[i][j] 记录 i 的 2^j 级别祖先
        int f[MAXN + 5][25];
        // dep[i] 求节点 i 的深度
        int dep[MAXN + 5];
        // void clear(){}
        void addEdge(int u, int v)
        {
            e[u].push_back(v);
            e[v].push_back(u);
        }
        void dfs(int u, int fa)
        {
            f[u][0] = fa;
            for (int i = 0; i < e[u].size(); i++)
            {
                int v = e[u][i];
                if (v == fa)
                    continue;
                dep[v] = dep[u] + 1;
                dfs(v, u);
            }
        }
        void init()
        {
            dep[s] = 0;
            dfs(s, 0); // 预处理深度 dep[u],以及一级祖先 f[u][0]
            // i 的 2^j 级别祖先 = i 的 2^{j-1} 级别祖先的 2^{j-1} 级别祖先
            for (int j = 1; (1LL << j) <= n; j++)
                for (int i = 1; i <= n; i++)
                    f[i][j] = f[f[i][j - 1]][j - 1];
        }
        int lca(int u, int v)
        {
            // 保证 u 在上面,v 在下面
            if (dep[v] < dep[u])
                swap(u, v);
            // 拉到同样的深度
            for (int j = 20; j >= 0; j--)
                if (dep[v] - dep[u] >= (1 << j))
                    v = f[v][j];
            // 初始两点之间是祖孙关系时,拉到同样深度就会变成同一个点
            if (u == v)
                return u;
            // 同步往上走,跳到了 lca 下面一跳位
            for (int j = 20; j >= 0; j--)
                if (f[u][j] != f[v][j])
                    u = f[u][j], v = f[v][j];
            return f[u][0];
        }
    }
    // Kruskal 重构树
    // 存所有边 <-权值, <起点,终点>>
    vector<pair<int, pair<int, int>>> allEdge;
    int fa[MAXN + 5];
    int findFa(int u)
    {
        if (fa[u] == u)
            return u;
        return fa[u] = findFa(fa[u]);
    }
    int ww[MAXN + 5]; // kruskal 重构树中每个点权值
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);
        cin >> F >> N >> M;
        // 真的建出来图
        for (int i = 1; i <= F; i++)
            for (int j = 1; j <= N; j++)
                for (int k = 1; k <= M; k++)
                {
                    for (int fx = 0; fx < 6; fx++)
                    {
                        int ii = i + dx[fx];
                        int jj = j + dy[fx];
                        int kk = k + dz[fx];
                        if (ii < 1 || ii > F ||
                            jj < 1 || jj > N ||
                            kk < 1 || kk > M)
                            continue;
                        int u = getId(i, j, k);
                        int v = getId(ii, jj, kk);
                        e[u].push_back(v);
                        e[v].push_back(u);
                    }
                }
        // 标黑色恐怖距离
        for (int i = 1; i <= F * M * N; i++)
            w[i] = -1;
        cin >> R;
        for (int i = 1; i <= R; i++)
        {
            int P, Q, R;
            cin >> P >> Q >> R;
            int u = getId(P, Q, R);
            q.push(u);
            w[u] = 0;
        }
        while (!q.empty())
        {
            int u = q.front();
            q.pop();
            for (int v : e[u])
                if (w[v] == -1)
                {
                    w[v] = w[u] + 1;
                    q.push(v);
                }
        }
        // Kruskal 重构树
        // 遍历每条边
        for (int u = 1; u <= F * M * N; u++)
            for (int v : e[u])
                allEdge.push_back({-min(w[u], w[v]), {u, v}});
        // 边从大到小排序(通过负边权实现)
        sort(allEdge.begin(), allEdge.end());
        // 并查集初始化
        for (int u = 1; u <= 2 * F * M * N; u++)
            fa[u] = u;
        // Kruskal,过程中把建立的新图放入 LCA
        LCA::n = F * N * M; // 初始的点数
        for (int i = 0; i < allEdge.size(); i++)
        {
            int nowW = -allEdge[i].first;
            int u = allEdge[i].second.first;
            int v = allEdge[i].second.second;
            int faU = findFa(u);
            int faV = findFa(v);
            if (faU == faV)
                continue;
            LCA::n++;
            ww[LCA::n] = nowW;
            ww[u] = w[u];
            ww[v] = w[v];
            LCA::addEdge(LCA::n, faU);
            LCA::addEdge(LCA::n, faV);
            fa[faU] = fa[faV] = LCA::n;
        }
        LCA::s = LCA::n;
        LCA::init();
        // 回答询问
        int Q;
        cin >> Q;
        while (Q--)
        {
            int a, b, c, x, y, z;
            cin >> a >> b >> c >> x >> y >> z;
            int u = getId(a, b, c);
            int v = getId(x, y, z);
            cout << ww[LCA::lca(u, v)] << "\n";
        }
        return 0;
    }
    
    • 1

    信息

    ID
    13299
    时间
    1000ms
    内存
    1024MiB
    难度
    5
    标签
    递交数
    51
    已通过
    4
    上传者