Open In App

Count Non-Path Triplets in a Tree

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:

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:

Below is the implementation for the above approach:




#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 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)




# 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)




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 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)


Article Tags :