Open In App

Count Non-Path Triplets in a Tree

Last Updated : 27 Nov, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

Given a tree with N vertices numbered from 1 to N. The ith edge connects vertex Ai to vertex Bi, the task is to find the number of tuples of integers
(i, j, k) such that:

  • i < j < k and 1 ≤ i, j, k ≤ N.
  • The given tree does not contain a simple path that includes all three vertices i, j, and k.

Examples:

Inputs: N = 5, A[] = [1, 2, 2, 1], B[] = [2, 3, 4, 5]
Output: 2
Explanation: Two tuples satisfy the conditions: (i, j, k) = (1, 3, 4), (3, 4, 5).

Inputs: N = 6, A[] = [1, 2, 3, 4, 5], B[] = [2, 3, 4, 5, 6]
Output: 0

Approach: To solve the problem follow the below observations:

Observations:

In this explanation, we discussed the method to satisfy a certain condition in a tree structure and provided a formula to calculate a sum involving fixed points.

  • Condition: The goal is to avoid having three consecutive vertices (a, b, c) on a simple path within the tree.
  • Fixed Point: When you have three consecutive points on a path, you can consider a fixed point in the middle. This helps in satisfying the conditions mentioned above.
  • Connected Components: The sizes of connected components, excluding certain specified points (m1, …, mk), are denoted as mi.
  • Sum of Fixed Points: The sum of fixed points is calculated using the formula 2 * Σ (i < j) mi * mj.
  • Transformation: The formula can be transformed as follows: 2 * Σ (i < j) mi * mj = ( Σ mi ) 2 – Σ (mi) 2.
  • Complexity: The problem can be solved efficiently with complexity O(k) by pre-calculating the size of the subtree rooted at each vertex v against v and O(n) overall.
  • No Operator Change: The problem can be solved using this approach without altering any mathematical operators.

Steps involved to solve the problem:

  • Create an empty adjacency list ‘T’ to represent the tree structure. For each edge in the tree, add the corresponding vertices to each other’s adjacency lists to represent the connections.
  • Initialize the variable ‘ans’ to n * (n – 1) * (n – 2) / 6, which represents the total number of tuples (i, j, k) with 1 ≤ i < j < k ≤ N.
  • Initialize a vector ‘sz’ of size ‘n’, where each element is initialized to 1. This vector will be used to store the size of each subtree rooted at every vertex.
  • Define a recursive function ‘dfs’ that takes two parameters ‘v’ (current vertex) and ‘pre’ (the parent vertex). The purpose of this function is to calculate the size of each subtree rooted at each vertex and update the ‘ans’ variable accordingly.
  • Inside the ‘dfs’ function, iterate through the adjacent vertices of ‘v’ (excluding the parent vertex ‘pre’). For each adjacent vertex ‘c’, recursively call the ‘dfs’ function with ‘c’ as the current vertex and ‘v’ as the parent vertex.
  • Calculate the ‘sum1‘ and ‘sum2‘ values for the current vertex ‘v’ based on the sizes of its subtrees and update the ‘sz’ vector accordingly.
  • Decrement ‘ans’ by (sum1 * sum1 – sum2) >> 1. This step removes the tuples (i, j, k) that form a simple path containing all three vertices i, j, and k in the tree.
  • Call the ‘dfs’ function with the starting vertex as 0 (or any other appropriate starting vertex) and the parent vertex as -1 (or any other value indicating that there is no parent).
  • Finally, print the ‘ans’ value, which represents the number of tuples (i, j, k) that satisfy the condition of not having a simple path containing all three vertices i, j, and k in the tree.

Below is the implementation for the above approach:

C++




#include <bits/stdc++.h>
using namespace std;
 
using ll = long long;
 
// Function to perform Depth-First Search
// (DFS) on the tree
void dfs(int v, int pre, vector<vector<int> >& T,
         vector<ll>& sz, ll n, ll& ans)
{
    ll sum1 = 0, sum2 = 0;
 
    // Traverse through the children
    // of vertex 'v'
    for (int c : T[v]) {
 
        // Skip the parent vertex 'pre'
        if (c == pre)
            continue;
 
        // Recursively call the 'dfs' function
        // for the child vertex 'c'
        dfs(c, v, T, sz, n, ans);
 
        // Update the sum1 with the size of
        // the subtree rooted at vertex 'c'
        sum1 += sz;
 
        // Update the sum2 with the square
        // of the size of the subtree
        // rooted at vertex 'c'
        sum2 += sz * sz;
 
        // Update the size of the subtree
        // rooted at vertex 'v'
        sz[v] += sz;
    }
 
    // Update sum1 with the size of the
    // subtree rooted at vertex 'v'
    // excluding vertex 'v'
    sum1 += n - sz[v];
 
    // Update sum2 with the square of
    // the size of the subtree rooted at
    // vertex 'v' excluding vertex 'v'
    sum2 += (n - sz[v]) * (n - sz[v]);
 
    // Update the 'ans' by subtracting
    // (sum1 * sum1 - sum2)/2. This removes the
    // tuples (i, j, k) forming a simple path
    // containing all three vertices i, j, and k
    // in the tree
    ans -= (sum1 * sum1 - sum2) >> 1;
}
 
// Driver code
int main()
{
    ll n = 5;
 
    // Adjacency list to represent the tree.
    // Index starts from 1.
    vector<vector<int> > T(n + 1);
 
    // Adding edges to the tree.
    // Edge between vertex 1 (A1) and vertex 2 (B1).
    T[1].push_back(2);
 
    // Reverse edge from vertex 2 to vertex 1.
    T[2].push_back(1);
 
    // Edge between vertex 2 (A2) and vertex 3 (B2).
    T[2].push_back(3);
 
    // Reverse edge from vertex 3 to vertex 2.
    T[3].push_back(2);
 
    // Edge between vertex 2 (A3) and vertex 4 (B3).
    T[2].push_back(4);
 
    // Reverse edge from vertex 4 to vertex 2.
    T[4].push_back(2);
 
    // Edge between vertex 1 (A4) and vertex 5 (B4)
    T[1].push_back(5);
 
    // Reverse edge from vertex 5 to vertex 1.
    T[5].push_back(1);
 
    // Calculate the initial value for 'ans'.
    ll ans = n * (n - 1) * (n - 2) / 6;
 
    // Initialize a vector 'sz' to store the
    // size of each subtree, where 'sz[i]'
    // represents the size of the subtree
    // rooted at vertex 'i'.
    vector<ll> sz(n + 1, 1);
 
    // Call the 'dfs' function starting from
    // vertex 1 (the valid root vertex) and the
    // parent vertex as -1 (indicating
    // there is no parent).
    dfs(1, -1, T, sz, n, ans);
 
    // Output the final answer.
    cout << ans << '\n';
    return 0;
}


Java




// Java code for the above approach
 
import java.util.*;
 
public class Main {
    static long ans = 0;
 
    // Function to perform Depth-First Search (DFS) on the tree
    static void dfs(int v, int pre, List<List<Integer>> T, List<Long> sz, long n) {
        long sum1 = 0, sum2 = 0;
 
        // Traverse through the children of vertex 'v'
        for (int c : T.get(v)) {
 
            // Skip the parent vertex 'pre'
            if (c == pre)
                continue;
 
            // Recursively call the 'dfs' function for the child vertex 'c'
            dfs(c, v, T, sz, n);
 
            // Update sum1 with the size of the subtree rooted at vertex 'c'
            sum1 += sz.get(c);
 
            // Update sum2 with the square of the size of the subtree rooted at vertex 'c'
            sum2 += sz.get(c) * sz.get(c);
 
            // Update the size of the subtree rooted at vertex 'v'
            sz.set(v, sz.get(v) + sz.get(c));
        }
 
        // Update sum1 with the size of the subtree rooted at vertex 'v' excluding vertex 'v'
        sum1 += n - sz.get(v);
 
        // Update sum2 with the square of the size of the subtree rooted at vertex 'v' excluding vertex 'v'
        sum2 += (n - sz.get(v)) * (n - sz.get(v));
 
        // Update 'ans' by subtracting (sum1 * sum1 - sum2)/2. This removes the tuples (i, j, k) forming a simple path containing all three vertices i, j, and k in the tree
        ans -= (sum1 * sum1 - sum2) / 2;
    }
 
    public static void main(String[] args) {
        long n = 5;
 
        // Adjacency list to represent the tree. Index starts from 1.
        List<List<Integer>> T = new ArrayList<>();
        for (int i = 0; i <= n; i++) {
            T.add(new ArrayList<>());
        }
 
        // Adding edges to the tree.
        // Edge between vertex 1 (A1) and vertex 2 (B1).
        T.get(1).add(2);
 
        // Reverse edge from vertex 2 to vertex 1.
        T.get(2).add(1);
 
        // Edge between vertex 2 (A2) and vertex 3 (B2).
        T.get(2).add(3);
 
        // Reverse edge from vertex 3 to vertex 2.
        T.get(3).add(2);
 
        // Edge between vertex 2 (A3) and vertex 4 (B3).
        T.get(2).add(4);
 
        // Reverse edge from vertex 4 to vertex 2.
        T.get(4).add(2);
 
        // Edge between vertex 1 (A4) and vertex 5 (B4)
        T.get(1).add(5);
 
        // Reverse edge from vertex 5 to vertex 1.
        T.get(5).add(1);
 
        // Calculate the initial value for 'ans'.
        ans = n * (n - 1) * (n - 2) / 6;
 
        // Initialize a list 'sz' to store the size of each subtree, where 'sz[i]' represents the size of the subtree rooted at vertex 'i'.
        List<Long> sz = new ArrayList<>();
        for (int i = 0; i <= n; i++) {
            sz.add(1L);
        }
 
        // Call the 'dfs' function starting from vertex 1 (the valid root vertex) and the parent vertex as -1 (indicating there is no parent).
        dfs(1, -1, T, sz, n);
 
        // Output the final answer.
        System.out.println(ans);
    }
}
 
// This code is contributed by Abhinav Mahajan (abhinav_m22)


Python3




# Python code for above approach
def dfs(v, pre, T, sz, n, ans):
    sum1 = 0
    sum2 = 0
 
    # Traverse through the children of vertex 'v'
    for c in T[v]:
        # Skip the parent vertex 'pre'
        if c == pre:
            continue
 
        # Recursively call the 'dfs' function
        # #for the child vertex 'c'
        dfs(c, v, T, sz, n, ans)
 
        # Update the sum1 with the size of
        # the subtree rooted at vertex 'c'
        sum1 += sz
 
        # Update the sum2 with the square of
        # the size of the subtree rooted at vertex 'c'
        sum2 += sz * sz
 
        # Update the size of the subtree
        # rooted at vertex 'v'
        sz[v] += sz
 
    # Update sum1 with the size of the subtree
    # rooted at vertex 'v' excluding vertex 'v'
    sum1 += n - sz[v]
 
    # Update sum2 with the square of the size of
    # the subtree rooted at vertex 'v' excluding vertex 'v'
    sum2 += (n - sz[v]) * (n - sz[v])
 
    # Update 'ans' by subtracting (sum1 * sum1 - sum2) / 2.
    ans[0] -= (sum1 * sum1 - sum2) // 2
 
# Driver code
def main():
    n = 5
    T = [[] for _ in range(n + 1)]
 
    # Adding edges to the tree
    edges = [(1, 2), (2, 3), (2, 4), (1, 5)]
    for A, B in edges:
        T[A].append(B)
        T[B].append(A)
 
    # Calculate the initial value for 'ans'
    ans = [n * (n - 1) * (n - 2) // 6]
 
    # Initialize a list 'sz' to store the size of each subtree
    sz = [1] * (n + 1)
 
    # Call the 'dfs' function
    dfs(1, -1, T, sz, n, ans)
 
    # Output the final answer
    print(ans[0])
 
if __name__ == "__main__":
    main()
 
# This code is contributed by Omkar N. Chorge (omkarchorge10)


C#




using System;
using System.Collections.Generic;
 
class GFG
{
    static long ans = 0;
 
    // Function to perform Depth-First Search (DFS) on the tree
    static void Dfs(int v, int pre, List<List<int>> T, List<long> sz, long n)
    {
        long sum1 = 0, sum2 = 0;
 
        // Traverse through the children of vertex 'v'
        foreach (int c in T[v])
        {
            // Skip the parent vertex 'pre'
            if (c == pre)
                continue;
 
            // Recursively call the 'Dfs' function for the child vertex 'c'
            Dfs(c, v, T, sz, n);
 
            // Update sum1 with the size of the subtree rooted at vertex 'c'
            sum1 += sz;
 
            // Update sum2 with the square of the size of the subtree rooted at vertex 'c'
            sum2 += sz * sz;
 
            // Update the size of the subtree rooted at vertex 'v'
            sz[v] += sz;
        }
 
        // Update sum1 with the size of the subtree rooted at vertex 'v' excluding vertex 'v'
        sum1 += n - sz[v];
 
        // Update sum2 with the square of the size of the subtree rooted at vertex
        // 'v' excluding vertex 'v'
        sum2 += (n - sz[v]) * (n - sz[v]);
 
        // Update 'ans' by subtracting (sum1 * sum1 - sum2)/2.
        // This removes the tuples (i, j, k) forming a simple path
        // containing all three vertices i, j, and k in the tree
        ans -= (sum1 * sum1 - sum2) / 2;
    }
 
    public static void Main(string[] args)
    {
        long n = 5;
 
        // Adjacency list to represent the tree. Index starts from 1.
        List<List<int>> T = new List<List<int>>();
        for (int i = 0; i <= n; i++)
        {
            T.Add(new List<int>());
        }
 
        // Adding edges to the tree.
        // Edge between vertex 1 (A1) and vertex 2 (B1).
        T[1].Add(2);
 
        // Reverse edge from vertex 2 to vertex 1.
        T[2].Add(1);
 
        // Edge between vertex 2 (A2) and vertex 3 (B2).
        T[2].Add(3);
 
        // Reverse edge from vertex 3 to vertex 2.
        T[3].Add(2);
 
        // Edge between vertex 2 (A3) and vertex 4 (B3).
        T[2].Add(4);
 
        // Reverse edge from vertex 4 to vertex 2.
        T[4].Add(2);
 
        // Edge between vertex 1 (A4) and vertex 5 (B4)
        T[1].Add(5);
 
        // Reverse edge from vertex 5 to vertex 1.
        T[5].Add(1);
 
        // Calculate the initial value for 'ans'.
        ans = n * (n - 1) * (n - 2) / 6;
 
        // Initialize a list 'sz' to store the size of each subtree,
        // where 'sz[i]' represents the size of the subtree rooted at vertex 'i'.
        List<long> sz = new List<long>();
        for (int i = 0; i <= n; i++)
        {
            sz.Add(1L);
        }
 
        // Call the 'Dfs' function starting from vertex 1 (the valid root vertex)
        //and the parent vertex as -1 (indicating there is no parent).
        Dfs(1, -1, T, sz, n);
 
        // Output the final answer.
        Console.WriteLine(ans);
    }
}


Javascript




// Javascript code for the above approach:
 
let ans = 0;
 
// Function to perform Depth-First Search (DFS) on the tree
function dfs(v, pre, T, sz, n) {
    let sum1 = 0, sum2 = 0;
 
    // Traverse through the children of vertex 'v'
    for (let c of T[v]) {
 
        // Skip the parent vertex 'pre'
        if (c === pre)
            continue;
 
        // Recursively call the 'dfs' function for the child vertex 'c'
        dfs(c, v, T, sz, n);
 
        // Update sum1 with the size of the subtree rooted at vertex 'c'
        sum1 += sz;
 
        // Update sum2 with the square of the size of the subtree rooted at vertex 'c'
        sum2 += sz * sz;
 
        // Update the size of the subtree rooted at vertex 'v'
        sz[v] += sz;
    }
 
    // Update sum1 with the size of the subtree rooted at vertex 'v' excluding vertex 'v'
    sum1 += n - sz[v];
 
    // Update sum2 with the square of the size of the subtree rooted at vertex 'v' excluding vertex 'v'
    sum2 += (n - sz[v]) * (n - sz[v]);
 
    // Update 'ans' by subtracting (sum1 * sum1 - sum2)/2. This removes the tuples (i, j, k) forming a simple path containing all three vertices i, j, and k in the tree
    ans -= (sum1 * sum1 - sum2) / 2;
}
 
// Main function
function main() {
    let n = 5;
 
    // Adjacency list to represent the tree. Index starts from 1.
    let T = [];
    for (let i = 0; i <= n; i++) {
        T.push([]);
    }
 
    // Adding edges to the tree
    T[1].push(2);
    T[2].push(1);
    T[2].push(3);
    T[3].push(2);
    T[2].push(4);
    T[4].push(2);
    T[1].push(5);
    T[5].push(1);
 
    // Calculate the initial value for 'ans'
    ans = n * (n - 1) * (n - 2) / 6;
 
    // Initialize a list 'sz' to store the size of each subtree, where 'sz[i]' represents the size of the subtree rooted at vertex 'i'
    let sz = Array.from({ length: n + 1 }, () => 1);
 
    // Call the 'dfs' function starting from vertex 1 (the valid root vertex) and the parent vertex as -1 (indicating there is no parent)
    dfs(1, -1, T, sz, n);
 
    // Output the final answer
    console.log(ans);
}
 
// Call the main function
main();


Output

2










Time Complexity: O(n)
Auxiliary Space: O(n)



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads