Count Path Visits for Each Node for Given Path Queries

Last Updated : 3 Oct, 2026

A tree has n nodes numbered 1 to n, connected by n - 1 edges given as edges[][], where edges[i] = [u, v] indicates a direct edge between nodes u and v.

You are also given queries[][], where each query [u, v] represents the unique path from node u to node v. Every node on this path ( including u and v) is counted once.

For each node, find how many times it was counted across all queries in queries[][].

Note: The tree is connected and acyclic, with exactly n - 1 edges.

Examples:

Input: n = 5, edges[][] = [[1, 2], [1, 3], [3, 4], [3, 5]], queries[][] = [[1, 3], [2, 5], [1, 4]]

2056958665

Output: [3, 1, 3, 1, 1]
Explanation:
- Query [1, 3]: the path visits nodes [1, 3].
- Query [2, 5]: the path visits nodes [2, 1, 3, 5].
- Query [1, 4]: the path visits nodes [1, 3, 4].
Node 1 is visited 3 times, node 2 is visited 1 time, node 3 is visited 3 times, and nodes 4 and 5 are each visited 1 time.

Input: n = 4, edges[][] = [[1, 2], [2, 3], [2, 4]], queries[][] = [[1, 3], [4, 1]]
Output: [2, 2, 1, 1]
Explanation:
- Query [1, 3]: the path visits nodes [1, 2, 3].
- Query [4, 1]: the path visits nodes [4, 2, 1].
Node 1 is visited 2 times, node 2 is visited 2 times, nodes 3 and 4 are each visited 1 time.

Input: n = 4, edges[][] = [[1, 2], [2, 3], [3, 4]], queries[][] = [[1, 3], [2, 3]]
Output: [1, 2, 2, 0]
Explanation:
- Query [1, 3]: the path visits nodes [1, 2, 3].
- Query [2, 3]: the path visits nodes [2, 3]. Node 1 is visited 1 time, node 2 is visited 2 times, node 3 is visited 2 times, and node 4 is not visited.

Try It Yourself
redirect icon

[Naive Approach] Traverse the Path for Every Query - O(n * q) Time and O(n) Space

The idea is to process each query independently by finding the unique path between its two given nodes. Once the path is found, increment the count of every node on it.

Since a tree has exactly one path between any two nodes, we use DFS to locate the path and update the corresponding node counts.

Working of the Approach:

  • Build an adjacency list to represent the tree.
  • For each query (u, v), run DFS from u to find the path to v.
  • While backtracking, identify the nodes that belong to the path and increment their counts.
  • After processing all queries, return the count of every node.
C++
#include 
using namespace std;

vector<int> countVisits(int n, vector<vector<int>>& edges,
                        vector<vector<int>>& queries) {
    vector<vector<int>> adj(n + 1);

    for (auto& edge : edges) {
        int u = edge[0];
        int v = edge[1];

        adj[u].push_back(v);
        adj[v].push_back(u);
    }

    vector<int> ans(n + 1, 0);

    for (auto& query : queries) {
        int u = query[0];
        int v = query[1];

        vector<int> parent(n + 1, -1);
        queue<int> q;

        q.push(u);
        parent[u] = 0;

        // Find the path from u to v using BFS.
        while (!q.empty()) {
            int node = q.front();
            q.pop();

            if (node == v)
                break;

            for (int next : adj[node]) {
                if (parent[next] == -1) {
                    parent[next] = node;
                    q.push(next);
                }
            }
        }

        // Move from v back to u and count every path node.
        int node = v;

        while (node != 0) {
            ans[node]++;
            node = parent[node];
        }
    }

    ans.erase(ans.begin());

    return ans;
}

int main() {
    int n = 5;

    vector<vector<int>> edges = {
        {1, 2},
        {1, 3},
        {3, 4},
        {3, 5}
    };

    vector<vector<int>> queries = {
        {1, 3},
        {2, 5},
        {1, 4}
    };

    vector<int> res = countVisits(n, edges, queries);

    cout << "[";

    for (int i = 0; i < res.size(); i++) {
        if (i > 0)
            cout << ", ";

        cout << res[i];
    }

    cout << "]";

    return 0;
}
Java
import java.util.ArrayDeque;
import java.util.ArrayList;

class GFG {
    static ArrayList<Integer> countVisits(int n, int[][] edges, int[][] queries) {
        ArrayList<ArrayList<Integer>> adj = new ArrayList<>();

        for (int i = 0; i <= n; i++) {
            adj.add(new ArrayList<>());
        }

        for (int[] edge : edges) {
            int u = edge[0];
            int v = edge[1];

            adj.get(u).add(v);
            adj.get(v).add(u);
        }

        int[] ans = new int[n + 1];

        for (int[] query : queries) {
            int u = query[0];
            int v = query[1];

            int[] parent = new int[n + 1];

            for (int i = 0; i <= n; i++) {
                parent[i] = -1;
            }

            ArrayDeque<Integer> q = new ArrayDeque<>();
            q.add(u);
            parent[u] = 0;

            // Find the path from u to v using BFS.
            while (!q.isEmpty()) {
                int node = q.poll();

                if (node == v)
                    break;

                for (int next : adj.get(node)) {
                    if (parent[next] == -1) {
                        parent[next] = node;
                        q.add(next);
                    }
                }
            }

            // Move from v back to u and count every path node.
            int node = v;

            while (node != 0) {
                ans[node]++;
                node = parent[node];
            }
        }

        ArrayList<Integer> res = new ArrayList<>();

        for (int i = 1; i <= n; i++) {
            res.add(ans[i]);
        }

        return res;
    }

    public static void main(String[] args) {
        int n = 5;

        int[][] edges = {
            {1, 2},
            {1, 3},
            {3, 4},
            {3, 5}
        };

        int[][] queries = {
            {1, 3},
            {2, 5},
            {1, 4}
        };

        ArrayList<Integer> res = countVisits(n, edges, queries);

        System.out.println(res);
    }
}
Python
from collections import deque


def countVisits(n, edges, queries):
    adj = [[] for _ in range(n + 1)]

    for u, v in edges:
        adj[u].append(v)
        adj[v].append(u)

    ans = [0] * (n + 1)

    for u, v in queries:
        parent = [-1] * (n + 1)
        q = deque([u])
        parent[u] = 0

        # Find the path from u to v using BFS.
        while q:
            node = q.popleft()

            if node == v:
                break

            for nextNode in adj[node]:
                if parent[nextNode] == -1:
                    parent[nextNode] = node
                    q.append(nextNode)

        # Move from v back to u and count every path node.
        node = v

        while node != 0:
            ans[node] += 1
            node = parent[node]

    return ans[1:]


if __name__ == "__main__":
    n = 5

    edges = [
        [1, 2],
        [1, 3],
        [3, 4],
        [3, 5]
    ]

    queries = [
        [1, 3],
        [2, 5],
        [1, 4]
    ]

    print(countVisits(n, edges, queries))
C#
using System;
using System.Collections.Generic;

class GFG {
    static List<int> countVisits(int n, int[][] edges, int[][] queries) {
        List<List<int>> adj = new List<List<int>>();

        for (int i = 0; i <= n; i++) {
            adj.Add(new List<int>());
        }

        foreach (int[] edge in edges) {
            int u = edge[0];
            int v = edge[1];

            adj[u].Add(v);
            adj[v].Add(u);
        }

        int[] ans = new int[n + 1];

        foreach (int[] query in queries) {
            int u = query[0];
            int v = query[1];

            int[] parent = new int[n + 1];
            Array.Fill(parent, -1);

            Queue<int> q = new Queue<int>();
            q.Enqueue(u);
            parent[u] = 0;

            // Find the path from u to v using BFS.
            while (q.Count > 0) {
                int node = q.Dequeue();

                if (node == v)
                    break;

                foreach (int next in adj[node]) {
                    if (parent[next] == -1) {
                        parent[next] = node;
                        q.Enqueue(next);
                    }
                }
            }

            // Move from v back to u and count every path node.
            int current = v;

            while (current != 0) {
                ans[current]++;
                current = parent[current];
            }
        }

        List<int> res = new List<int>();

        for (int i = 1; i <= n; i++) {
            res.Add(ans[i]);
        }

        return res;
    }

    public static void Main() {
        int n = 5;

        int[][] edges = {
            new int[] {1, 2},
            new int[] {1, 3},
            new int[] {3, 4},
            new int[] {3, 5}
        };

        int[][] queries = {
            new int[] {1, 3},
            new int[] {2, 5},
            new int[] {1, 4}
        };

        List<int> res = countVisits(n, edges, queries);

        Console.WriteLine("[" + string.Join(", ", res) + "]");
    }
}
JavaScript
function countVisits(n, edges, queries) {
    const adj = Array.from({ length: n + 1 }, () => []);

    for (const [u, v] of edges) {
        adj[u].push(v);
        adj[v].push(u);
    }

    const ans = new Array(n + 1).fill(0);

    for (const [u, v] of queries) {
        const parent = new Array(n + 1).fill(-1);
        const queue = [u];
        let front = 0;

        parent[u] = 0;

        // Find the path from u to v using BFS.
        while (front < queue.length) {
            const node = queue[front++];

            if (node === v)
                break;

            for (const next of adj[node]) {
                if (parent[next] === -1) {
                    parent[next] = node;
                    queue.push(next);
                }
            }
        }

        // Move from v back to u and count every path node.
        let node = v;

        while (node !== 0) {
            ans[node]++;
            node = parent[node];
        }
    }

    return ans.slice(1);
}

// Driver Code
const n = 5;

const edges = [
    [1, 2],
    [1, 3],
    [3, 4],
    [3, 5]
];

const queries = [
    [1, 3],
    [2, 5],
    [1, 4]
];

const res = countVisits(n, edges, queries);

console.log("[" + res.join(", ") + "]");

Output
[3, 1, 3, 1, 1]

[Expected Approach] Mark Paths with LCA and Tree Difference Array - O((n + q) log n) Time and O(n log n) Space

The idea is to preprocess the tree for LCA queries using binary lifting, then use a tree difference array to mark the endpoints and LCA of each path.

After processing all queries, one bottom-up traversal accumulates these values to obtain the number of times each node lies on a queried path.

Working of the Approach:

  • Root the tree and compute depth[], parent[], and the binary lifting table for every node.
  • For each query (u, v), find lca, then update diff[u]++, diff[v]++, diff[lca]--, and diff[parent[lca]]-- when parent[lca] exists.
  • Traverse the tree from leaves towards the root and add each node's diff value to its parent.
  • After this accumulation, diff[i] gives the number of queried paths that pass through node i.

Let us understand with an example:

Input: n = 5, edges[][] = [[1, 2], [1, 3], [3, 4], [3, 5]], queries[][] = [[1, 3], [2, 5], [1, 4]]

2056958665
  • Compute the LCA of each query: LCA(1, 3) = 1, LCA(2, 5) = 1, and LCA(1, 4) = 1.
  • For query [1, 3], update diff[1] += 1, diff[3] += 1, and diff[1] -= 1. This marks the path 1 -> 3.
  • For query [2, 5], update diff[2] += 1, diff[5] += 1, and diff[1] -= 1. This marks the path 2 -> 1 -> 3 -> 5.
  • For query [1, 4], update diff[1] += 1, diff[4] += 1, and diff[1] -= 1. This marks the path 1 -> 3 -> 4.
  • After processing all queries, accumulate diff[] from children to parents: node 4 contributes to 3, node 5 contributes to 3, and nodes 2 and 3 contribute to 1.
  • The final values become [3, 1, 3, 1, 1], which are exactly the number of times each node appears on the queried paths.
C++
#include 
using namespace std;

vector<int> countVisits(int n, vector<vector<int>>& edges,
                        vector<vector<int>>& queries) {
    int LOG = 1;

    while ((1 << LOG) <= n)
        LOG++;

    vector<vector<int>> adj(n + 1);

    for (auto& edge : edges) {
        int u = edge[0];
        int v = edge[1];

        adj[u].push_back(v);
        adj[v].push_back(u);
    }

    vector<vector<int>> up(LOG, vector<int>(n + 1));
    vector<int> depth(n + 1);
    vector<int> order;

    order.reserve(n);

    // Build parent, depth and traversal order from the root.
    queue<int> q;
    q.push(1);
    up[0][1] = 0;

    while (!q.empty()) {
        int node = q.front();
        q.pop();

        order.push_back(node);

        for (int next : adj[node]) {
            if (next == up[0][node])
                continue;

            up[0][next] = node;
            depth[next] = depth[node] + 1;
            q.push(next);
        }
    }

    // Build the binary lifting table.
    for (int j = 1; j < LOG; j++) {
        for (int node = 1; node <= n; node++) {
            up[j][node] = up[j - 1][up[j - 1][node]];
        }
    }

    auto lca = [&](int u, int v) {
        if (depth[u] < depth[v])
            swap(u, v);

        int diffDepth = depth[u] - depth[v];

        for (int j = 0; j < LOG; j++) {
            if (diffDepth & (1 << j))
                u = up[j][u];
        }

        if (u == v)
            return u;

        for (int j = LOG - 1; j >= 0; j--) {
            if (up[j][u] != up[j][v]) {
                u = up[j][u];
                v = up[j][v];
            }
        }

        return up[0][u];
    };

    vector<int> diff(n + 1, 0);

    for (auto& query : queries) {
        int u = query[0];
        int v = query[1];

        int ancestor = lca(u, v);

        // Mark the endpoints and remove the contribution above the LCA.
        diff[u]++;
        diff[v]++;
        diff[ancestor]--;

        if (up[0][ancestor] != 0)
            diff[up[0][ancestor]]--;
    }

    // Accumulate child contributions towards the root.
    for (int i = n - 1; i > 0; i--) {
        int node = order[i];
        diff[up[0][node]] += diff[node];
    }

    diff.erase(diff.begin());

    return diff;
}

int main() {
    int n = 5;

    vector<vector<int>> edges = {
        {1, 2},
        {1, 3},
        {3, 4},
        {3, 5}
    };

    vector<vector<int>> queries = {
        {1, 3},
        {2, 5},
        {1, 4}
    };

    vector<int> res = countVisits(n, edges, queries);

    cout << "[";

    for (int i = 0; i < res.size(); i++) {
        if (i > 0)
            cout << ", ";

        cout << res[i];
    }

    cout << "]";

    return 0;
}
Java
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;

class GFG {
    static ArrayList<Integer> countVisits(int n, int[][] edges, int[][] queries) {
        int LOG = 1;

        while ((1 << LOG) <= n)
            LOG++;

        ArrayList<ArrayList<Integer>> adj = new ArrayList<>();

        for (int i = 0; i <= n; i++)
            adj.add(new ArrayList<>());

        for (int[] edge : edges) {
            int u = edge[0];
            int v = edge[1];

            adj.get(u).add(v);
            adj.get(v).add(u);
        }

        int[][] up = new int[LOG][n + 1];
        int[] depth = new int[n + 1];
        int[] order = new int[n];
        int orderSize = 0;

        ArrayDeque<Integer> q = new ArrayDeque<>();
        q.add(1);

        // Build parent, depth and traversal order from the root.
        while (!q.isEmpty()) {
            int node = q.poll();
            order[orderSize++] = node;

            for (int next : adj.get(node)) {
                if (next == up[0][node])
                    continue;

                up[0][next] = node;
                depth[next] = depth[node] + 1;
                q.add(next);
            }
        }

        // Build the binary lifting table.
        for (int j = 1; j < LOG; j++) {
            for (int node = 1; node <= n; node++) {
                up[j][node] = up[j - 1][up[j - 1][node]];
            }
        }

        int[] diff = new int[n + 1];

        for (int[] query : queries) {
            int u = query[0];
            int v = query[1];

            int ancestor = findLca(u, v, depth, up, LOG);

            // Mark the endpoints and remove the contribution above the LCA.
            diff[u]++;
            diff[v]++;
            diff[ancestor]--;

            if (up[0][ancestor] != 0)
                diff[up[0][ancestor]]--;
        }

        // Accumulate child contributions towards the root.
        for (int i = orderSize - 1; i > 0; i--) {
            int node = order[i];
            diff[up[0][node]] += diff[node];
        }

        ArrayList<Integer> res = new ArrayList<>();

        for (int i = 1; i <= n; i++)
            res.add(diff[i]);

        return res;
    }

    static int findLca(int u, int v, int[] depth,
                       int[][] up, int LOG) {
        if (depth[u] < depth[v]) {
            int temp = u;
            u = v;
            v = temp;
        }

        int diffDepth = depth[u] - depth[v];

        for (int j = 0; j < LOG; j++) {
            if ((diffDepth & (1 << j)) != 0)
                u = up[j][u];
        }

        if (u == v)
            return u;

        for (int j = LOG - 1; j >= 0; j--) {
            if (up[j][u] != up[j][v]) {
                u = up[j][u];
                v = up[j][v];
            }
        }

        return up[0][u];
    }

    public static void main(String[] args) {
        int n = 5;

        int[][] edges = {
            {1, 2},
            {1, 3},
            {3, 4},
            {3, 5}
        };

        int[][] queries = {
            {1, 3},
            {2, 5},
            {1, 4}
        };

        ArrayList<Integer> res = countVisits(n, edges, queries);

        System.out.println(res);
    }
}
Python
from collections import deque


def countVisits(n, edges, queries):
    LOG = n.bit_length()

    adj = [[] for _ in range(n + 1)]

    for u, v in edges:
        adj[u].append(v)
        adj[v].append(u)

    up = [[0] * (n + 1) for _ in range(LOG)]
    depth = [0] * (n + 1)
    order = []

    q = deque([1])

    # Build parent, depth and traversal order from the root.
    while q:
        node = q.popleft()
        order.append(node)

        for next_node in adj[node]:
            if next_node == up[0][node]:
                continue

            up[0][next_node] = node
            depth[next_node] = depth[node] + 1
            q.append(next_node)

    # Build the binary lifting table.
    for j in range(1, LOG):
        for node in range(1, n + 1):
            up[j][node] = up[j - 1][up[j - 1][node]]

    def findLca(u, v):
        if depth[u] < depth[v]:
            u, v = v, u

        diff_depth = depth[u] - depth[v]

        for j in range(LOG):
            if diff_depth & (1 << j):
                u = up[j][u]

        if u == v:
            return u

        for j in range(LOG - 1, -1, -1):
            if up[j][u] != up[j][v]:
                u = up[j][u]
                v = up[j][v]

        return up[0][u]

    diff = [0] * (n + 1)

    for u, v in queries:
        ancestor = findLca(u, v)

        # Mark the endpoints and remove the contribution above the LCA.
        diff[u] += 1
        diff[v] += 1
        diff[ancestor] -= 1

        if up[0][ancestor] != 0:
            diff[up[0][ancestor]] -= 1

    # Accumulate child contributions towards the root.
    for node in reversed(order[1:]):
        diff[up[0][node]] += diff[node]

    return diff[1:]


if __name__ == "__main__":
    n = 5

    edges = [
        [1, 2],
        [1, 3],
        [3, 4],
        [3, 5]
    ]

    queries = [
        [1, 3],
        [2, 5],
        [1, 4]
    ]

    print(countVisits(n, edges, queries))
C#
using System;
using System.Collections.Generic;

class GFG {
    static List<int> countVisits(int n, int[][] edges, int[][] queries) {
        int LOG = 1;

        while ((1 << LOG) <= n)
            LOG++;

        List<List<int>> adj = new List<List<int>>();

        for (int i = 0; i <= n; i++)
            adj.Add(new List<int>());

        foreach (int[] edge in edges) {
            int u = edge[0];
            int v = edge[1];

            adj[u].Add(v);
            adj[v].Add(u);
        }

        int[,] up = new int[LOG, n + 1];
        int[] depth = new int[n + 1];
        int[] order = new int[n];
        int orderSize = 0;

        Queue<int> q = new Queue<int>();
        q.Enqueue(1);

        // Build parent, depth and traversal order from the root.
        while (q.Count > 0) {
            int node = q.Dequeue();
            order[orderSize++] = node;

            foreach (int next in adj[node]) {
                if (next == up[0, node])
                    continue;

                up[0, next] = node;
                depth[next] = depth[node] + 1;
                q.Enqueue(next);
            }
        }

        // Build the binary lifting table.
        for (int j = 1; j < LOG; j++) {
            for (int node = 1; node <= n; node++) {
                up[j, node] = up[j - 1, up[j - 1, node]];
            }
        }

        int[] diff = new int[n + 1];

        foreach (int[] query in queries) {
            int u = query[0];
            int v = query[1];

            int ancestor = findLca(u, v, depth, up, LOG);

            // Mark the endpoints and remove the contribution above the LCA.
            diff[u]++;
            diff[v]++;
            diff[ancestor]--;

            if (up[0, ancestor] != 0)
                diff[up[0, ancestor]]--;
        }

        // Accumulate child contributions towards the root.
        for (int i = orderSize - 1; i > 0; i--) {
            int node = order[i];
            diff[up[0, node]] += diff[node];
        }

        List<int> res = new List<int>();

        for (int i = 1; i <= n; i++)
            res.Add(diff[i]);

        return res;
    }

    static int findLca(int u, int v, int[] depth,
                       int[,] up, int LOG) {
        if (depth[u] < depth[v]) {
            int temp = u;
            u = v;
            v = temp;
        }

        int diffDepth = depth[u] - depth[v];

        for (int j = 0; j < LOG; j++) {
            if ((diffDepth & (1 << j)) != 0)
                u = up[j, u];
        }

        if (u == v)
            return u;

        for (int j = LOG - 1; j >= 0; j--) {
            if (up[j, u] != up[j, v]) {
                u = up[j, u];
                v = up[j, v];
            }
        }

        return up[0, u];
    }

    public static void Main() {
        int n = 5;

        int[][] edges = {
            new int[] {1, 2},
            new int[] {1, 3},
            new int[] {3, 4},
            new int[] {3, 5}
        };

        int[][] queries = {
            new int[] {1, 3},
            new int[] {2, 5},
            new int[] {1, 4}
        };

        List<int> res = countVisits(n, edges, queries);

        Console.WriteLine("[" + string.Join(", ", res) + "]");
    }
}
JavaScript
function countVisits(n, edges, queries) {
    let LOG = 1;

    while ((1 << LOG) <= n)
        LOG++;

    const adj = Array.from({ length: n + 1 }, () => []);

    for (const [u, v] of edges) {
        adj[u].push(v);
        adj[v].push(u);
    }

    const up = Array.from(
        { length: LOG },
        () => new Array(n + 1).fill(0)
    );

    const depth = new Array(n + 1).fill(0);
    const order = [];

    const queue = [1];
    let front = 0;

    // Build parent, depth and traversal order from the root.
    while (front < queue.length) {
        const node = queue[front++];
        order.push(node);

        for (const next of adj[node]) {
            if (next === up[0][node])
                continue;

            up[0][next] = node;
            depth[next] = depth[node] + 1;
            queue.push(next);
        }
    }

    // Build the binary lifting table.
    for (let j = 1; j < LOG; j++) {
        for (let node = 1; node <= n; node++) {
            up[j][node] = up[j - 1][up[j - 1][node]];
        }
    }

    function findLca(u, v) {
        if (depth[u] < depth[v]) {
            [u, v] = [v, u];
        }

        let diffDepth = depth[u] - depth[v];

        for (let j = 0; j < LOG; j++) {
            if (diffDepth & (1 << j)) {
                u = up[j][u];
            }
        }

        if (u === v)
            return u;

        for (let j = LOG - 1; j >= 0; j--) {
            if (up[j][u] !== up[j][v]) {
                u = up[j][u];
                v = up[j][v];
            }
        }

        return up[0][u];
    }

    const diff = new Array(n + 1).fill(0);

    for (const [u, v] of queries) {
        const ancestor = findLca(u, v);

        // Mark the endpoints and remove the contribution above the LCA.
        diff[u]++;
        diff[v]++;
        diff[ancestor]--;

        if (up[0][ancestor] !== 0) {
            diff[up[0][ancestor]]--;
        }
    }

    // Accumulate child contributions towards the root.
    for (let i = order.length - 1; i > 0; i--) {
        const node = order[i];
        diff[up[0][node]] += diff[node];
    }

    return diff.slice(1);
}

// Driver Code
const n = 5;

const edges = [
    [1, 2],
    [1, 3],
    [3, 4],
    [3, 5]
];

const queries = [
    [1, 3],
    [2, 5],
    [1, 4]
];

const res = countVisits(n, edges, queries);

console.log("[" + res.join(", ") + "]");

Output
[3, 1, 3, 1, 1]
Comment