結果

問題 No.2337 Equidistant
ユーザー qwewe
提出日時 2025-05-14 12:55:52
言語 C++17
(gcc 13.3.0 + boost 1.87.0)
結果
AC  
実行時間 440 ms / 4,000 ms
コード長 10,142 bytes
コンパイル時間 670 ms
コンパイル使用メモリ 95,548 KB
実行使用メモリ 46,592 KB
最終ジャッジ日時 2025-05-14 12:57:28
合計ジャッジ時間 7,363 ms
ジャッジサーバーID
(参考情報)
judge4 / judge1
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
sample AC * 1
other AC * 28
権限があれば一括ダウンロードができます

ソースコード

diff #

#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
#include <vector>

// Use MAXN slightly larger than 200,000 for safety, e.g., 200005
const int MAXN = 200005; 
// log2(200000) is approx 17.6, so 18 levels are needed for binary lifting.
const int LOGN = 18; 

std::vector<int> adj[MAXN]; // Adjacency list for the tree
int parent[MAXN];           // Stores the direct parent of each node in DFS traversal rooted at 1
int depth[MAXN];            // Stores the depth of each node (distance from root 1)
int subtree_size[MAXN];     // Stores the size of the subtree rooted at each node in the DFS tree
int up[MAXN][LOGN];         // Binary lifting table: up[u][i] is the 2^i-th ancestor of u

/**
 * @brief Performs Depth First Search starting from node u to compute properties needed for LCA and subtree calculations.
 * 
 * @param u Current node being visited.
 * @param p Parent of the current node in DFS traversal.
 * @param d Depth of the current node.
 */
void dfs(int u, int p, int d) {
    parent[u] = p;         // Set parent
    depth[u] = d;          // Set depth
    subtree_size[u] = 1;   // Initialize subtree size with the node itself
    up[u][0] = p;          // Base case for binary lifting: 2^0 = 1st ancestor is the parent

    // Precompute higher ancestors using binary lifting dynamic programming
    for (int i = 1; i < LOGN; ++i) {
        // The 2^i-th ancestor is the 2^(i-1)-th ancestor of the 2^(i-1)-th ancestor
        // Check if the (i-1)-th ancestor exists (is not 0, our dummy node above root)
        if (up[u][i-1] != 0) { 
            up[u][i] = up[up[u][i-1]][i-1];
        } else {
            // If ancestor at level 2^(i-1) doesn't exist, then ancestor at level 2^i also doesn't exist
            up[u][i] = 0; 
        }
    }

    // Recurse for children
    for (int v : adj[u]) {
        if (v != p) { // Avoid going back up to the parent
            dfs(v, u, d + 1);
            subtree_size[u] += subtree_size[v]; // Update subtree size by adding sizes of children's subtrees
        }
    }
}

/**
 * @brief Finds the k-th ancestor of node u using binary lifting.
 * 
 * @param u The node whose ancestor is to be found.
 * @param k The level of ancestor required (k=1 means parent, k=2 means grandparent etc.).
 * @return The k-th ancestor node ID, or 0 if the k-th ancestor is above the root.
 */
int get_ancestor(int u, int k) {
    // Check for invalid k or if u is already 0 (a possible state if previous jump went above root)
    if (k < 0 || u == 0) return 0; 
    if (k == 0) return u; // 0-th ancestor is the node itself
    
    // Use binary lifting to jump up k levels efficiently
    for (int i = LOGN - 1; i >= 0; --i) {
        // If 2^i is less than or equal to the remaining distance k
        // This checks if the i-th bit of k is set
        if ((k >> i) & 1) { 
            u = up[u][i];  // Jump up by 2^i levels
            // If we jumped above the root (reached node 0), stop
            if (u == 0) break; 
        }
    }
    return u; // Return the k-th ancestor found
}

/**
 * @brief Finds the Lowest Common Ancestor (LCA) of nodes u and v.
 * 
 * @param u First node.
 * @param v Second node.
 * @return The LCA node ID.
 */
int lca(int u, int v) {
    // Ensure u is the deeper node by swapping if necessary
    if (depth[u] < depth[v]) {
        std::swap(u, v);
    }
    
    // Bring u up to the same depth as v using get_ancestor
    u = get_ancestor(u, depth[u] - depth[v]);

    // If u and v are the same node now, they are the LCA
    if (u == v) {
        return u;
    }

    // Jump u and v up simultaneously using binary lifting
    // Find the highest ancestors of u and v that are different
    for (int i = LOGN - 1; i >= 0; --i) {
        if (up[u][i] != up[v][i]) { // If 2^i-th ancestors are different
            u = up[u][i]; // Jump u up
            v = up[v][i]; // Jump v up
        }
    }
    // After the loop, u and v are direct children of the LCA
    // The LCA is the parent of u (or v)
    return up[u][0]; 
}

/**
 * @brief Calculates the distance (number of edges) between nodes u and v in the tree.
 * 
 * @param u First node.
 * @param v Second node.
 * @return The distance between u and v.
 */
int dist(int u, int v) {
    int common_ancestor = lca(u, v);
    // Standard tree distance formula: dist(u,v) = depth(u) + depth(v) - 2*depth(lca(u,v))
    return depth[u] + depth[v] - 2 * depth[common_ancestor];
}

int main() {
    // Use fast I/O operations
    std::ios_base::sync_with_stdio(false);
    std::cin.tie(NULL);

    int N; // Number of vertices
    int Q; // Number of queries
    std::cin >> N >> Q;

    // Read edges and build adjacency list representation of the tree
    for (int i = 0; i < N - 1; ++i) {
        int u, v;
        std::cin >> u >> v;
        adj[u].push_back(v);
        adj[v].push_back(u);
    }

    // Precomputation step: Perform DFS starting from root 1
    // This computes depths, parents, subtree sizes, and initializes binary lifting table
    dfs(1, 0, 0); // Assuming root is 1, its parent is 0 (dummy node), depth is 0

    // Process each query
    for (int i = 0; i < Q; ++i) {
        int S, T; // Query vertices
        std::cin >> S >> T;

        // Calculate distance between S and T
        int d_ST = dist(S, T);

        // If distance is odd, no vertex can be equidistant. The answer is 0.
        if (d_ST % 2 != 0) {
            std::cout << 0 << "\n";
        } else {
            // If distance is even, there is a unique midpoint vertex M on the path S-T.
            int k = d_ST / 2; // Half the distance
            int common_ancestor = lca(S, T); // Find LCA of S and T
            int d_S_L = depth[S] - depth[common_ancestor]; // Distance from S to LCA
            
            int M; // The midpoint vertex M
            // Determine the midpoint vertex M based on its position relative to S, T, and LCA
            if (d_S_L == k) { // Case 1: M is the LCA itself. This happens when S and T are equidistant from LCA.
                M = common_ancestor;
            } else if (d_S_L > k) { // Case 2: M is on the path from S to LCA (M is a proper ancestor of S). S is further than k from LCA.
                M = get_ancestor(S, k); // M is the k-th ancestor of S
            } else { // Case 3: M is on the path from LCA to T (M is a proper ancestor of T). S is closer than k to LCA.
                // Since d(S,M)=k and path is S->LCA->M, d(S,LCA)+d(LCA,M)=k.
                // Also d(T,M)=k. M is k-th ancestor of T.
                M = get_ancestor(T, k); // M is the k-th ancestor of T
            }

            // Find neighbors P_S and P_T of M on the unique path between S and T.
            // P_S is the neighbor towards S, P_T is the neighbor towards T.
            int P_S, P_T;
            
            // Determine P_S and P_T based on which case M falls into.
            if (M == common_ancestor) { // Case 1: M = LCA
                 // Path: S... -> P_S -> M -> P_T -> ...T
                 // P_S is the child of M towards S. P_T is the child of M towards T.
                 // Since d(S, M)=k, P_S is (k-1)th ancestor of S. (k>=1 because S!=T)
                 P_S = get_ancestor(S, k - 1);
                 // Since d(T, M)=k, P_T is (k-1)th ancestor of T.
                 P_T = get_ancestor(T, k - 1);
            } else if (lca(S, M) == M) { // Case 2: M is an ancestor of S (and M != LCA, since d_S_L > k). Path S -> M -> LCA -> T.
                 // Path: S... -> P_S -> M -> P_T -> ...LCA -> ...T
                 // P_S is child of M towards S. P_T is parent of M towards LCA.
                 P_S = get_ancestor(S, k - 1); // P_S is (k-1)th ancestor of S
                 P_T = parent[M]; // P_T is the parent of M based on DFS tree rooted at 1
            } else { // Case 3: M is an ancestor of T (and M != LCA, since d_S_L < k). Path S -> LCA -> M -> T.
                 // Path: S... -> LCA -> ... -> P_S -> M -> P_T -> ...T
                 // P_S is parent of M towards LCA. P_T is child of M towards T.
                 P_S = parent[M]; // P_S is the parent of M
                 P_T = get_ancestor(T, k - 1); // P_T is (k-1)th ancestor of T
            }

            // Compute sizes of components/subtrees associated with P_S and P_T relative to M.
            // These represent the sets of vertices closer to S and T respectively.
            long long size_M_PS, size_M_PT;
            
            // Calculate size of the component containing P_S if edge (M, P_S) is removed.
            // This depends on whether M is parent of P_S or vice-versa in the DFS tree.
            if (P_S == 0) { // Edge case check, should not happen if S!=T, D>=2
               size_M_PS = 0; 
            } else if (parent[P_S] == M) { // If M is the parent of P_S in the rooted DFS tree
                size_M_PS = (long long)subtree_size[P_S]; // The size is simply the precomputed subtree size of P_S
            } else { // P_S must be the parent of M
                // The component containing P_S consists of all nodes *except* those in the subtree rooted at M.
                size_M_PS = (long long)N - subtree_size[M]; 
            }

            // Calculate size of the component containing P_T if edge (M, P_T) is removed.
            if (P_T == 0) { // Edge case check
                size_M_PT = 0;
            } else if (parent[P_T] == M) { // If M is the parent of P_T
                 size_M_PT = (long long)subtree_size[P_T]; // Size is the subtree size of P_T
            } else { // P_T must be the parent of M
                 size_M_PT = (long long)N - subtree_size[M]; // Size is N minus the subtree size of M
            }
            
            // The number of vertices X such that d(X,S)=d(X,T) is the total number of vertices N
            // minus the sizes of the components "closer" to S (rooted at P_S relative to M)
            // and "closer" to T (rooted at P_T relative to M).
            long long total_nodes = N;
            long long answer = total_nodes - size_M_PS - size_M_PT;
            std::cout << answer << "\n";
        }
    }

    return 0;
}
0