Number of Ways to Assign Edge Weights I
MedPrompt
There is an undirected tree with n nodes labeled from 1 to n, rooted at node 1. The tree is represented by a 2D integer array edges of length n - 1, where edges[i] = [ui, vi] indicates that there is an edge between nodes ui and vi.
Initially, all edges have a weight of 0. You must assign each edge a weight of either 1 or 2.
The cost of a path between any two nodes u and v is the total weight of all edges in the path connecting them.
Select any one node x at the maximum depth. Return the number of ways to assign edge weights in the path from node 1 to x such that its total cost is odd.
Since the answer may be large, return it modulo 109 + 7.
Note: Ignore all edges not in the path from node 1 to x.
Example 1:

Input: edges = [[1,2]]
Output: 1
Explanation:
- The path from Node 1 to Node 2 consists of one edge (
1 → 2). - Assigning weight 1 makes the cost odd, while 2 makes it even. Thus, the number of valid assignments is 1.
Example 2:

Input: edges = [[1,2],[1,3],[3,4],[3,5]]
Output: 2
Explanation:
- The maximum depth is 2, with nodes 4 and 5 at the same depth. Either node can be selected for processing.
- For example, the path from Node 1 to Node 4 consists of two edges (
1 → 3and3 → 4). - Assigning weights (1,2) or (2,1) results in an odd cost. Thus, the number of valid assignments is 2.
Constraints:
2 <= n <= 105edges.length == n - 1edges[i] == [ui, vi]1 <= ui, vi <= nedgesrepresents a valid tree.
Approaches
3 approaches with complexity analysis and trade-offs.
This approach first determines the maximum depth of the tree, then explicitly generates every possible weight assignment for a path of that length using recursion. It checks each assignment's total cost and counts how many are odd. This method directly simulates the problem statement but is computationally very expensive.
Algorithm
- Build Graph: Construct an adjacency list representation of the tree from the
edgesarray. - Find Max Depth (L): Perform a Breadth-First Search (BFS) or Depth-First Search (DFS) starting from the root (node 1) to find the maximum depth
Lof any node in the tree. The path from the root to such a node will haveLedges. - Recursive Enumeration: Create a recursive function, say
countWays(edgeIndex, currentParity), whereedgeIndexis the current edge on the path (from 0 to L-1) andcurrentParityis the parity of the sum so far (0 for even, 1 for odd).- The base case is when
edgeIndex == L. IfcurrentParityis 1 (odd), return 1; otherwise, return 0. - In the recursive step, for the
edgeIndex-th edge, explore both possibilities:- Assign weight 1 (odd): This flips the parity. Recursively call
countWays(edgeIndex + 1, 1 - currentParity). - Assign weight 2 (even): This preserves the parity. Recursively call
countWays(edgeIndex + 1, currentParity).
- Assign weight 1 (odd): This flips the parity. Recursively call
- The total ways is the sum of the results from these two recursive calls (modulo
10^9 + 7).
- The base case is when
- Initial Call: Start the recursion with
countWays(0, 0)(0 edges processed, sum is 0 which is even). - Return Result: The final count is the answer.
Walkthrough
The core idea is to solve the problem by exploring all possibilities. First, we need to know the number of edges in the path, which is simply the maximum depth of the tree, let's call it L. We can find L using a standard graph traversal like BFS starting from the root (node 1). Once L is known, we can define a recursive function that tries assigning weight 1 and weight 2 to each of the L edges. For each of the 2^L total combinations, we calculate the sum and check its parity. A counter is incremented for each combination that results in an odd sum.
class Solution { private long MOD = 1000000007; public int numberOfWays(int[][] edges) { int n = edges.length + 1; if (n <= 1) return 0; List<List<Integer>> adj = new ArrayList<>(); for (int i = 0; i <= n; i++) { adj.add(new ArrayList<>()); } for (int[] edge : edges) { adj.get(edge[0]).add(edge[1]); adj.get(edge[1]).add(edge[0]); } int maxDepth = findMaxDepth(n, adj); if (maxDepth == 0) return 0; return (int) countRecursive(0, maxDepth, 0); } private long countRecursive(int edgeIndex, int L, int currentParity) { if (edgeIndex == L) { return currentParity; // Returns 1 if odd, 0 if even } // Case 1: Assign weight 1 (odd) long ways1 = countRecursive(edgeIndex + 1, L, 1 - currentParity); // Case 2: Assign weight 2 (even) long ways2 = countRecursive(edgeIndex + 1, L, currentParity); return (ways1 + ways2) % MOD; } private int findMaxDepth(int n, List<List<Integer>> adj) { Queue<int[]> queue = new LinkedList<>(); queue.offer(new int[]{1, 0}); // {node, depth} boolean[] visited = new boolean[n + 1]; visited[1] = true; int maxDepth = 0; while (!queue.isEmpty()) { int[] current = queue.poll(); int u = current[0]; int depth = current[1]; maxDepth = Math.max(maxDepth, depth); for (int v : adj.get(u)) { if (!visited[v]) { visited[v] = true; queue.offer(new int[]{v, depth + 1}); } } } return maxDepth; }}Complexity
Time
O(n + 2^L). Finding the max depth `L` takes `O(n)` time using BFS. The recursive function has two branches at each of the `L` levels, leading to `O(2^L)` calls. This is exponential and too slow for the given constraints.
Space
O(n + L). `O(n)` for the adjacency list and BFS data structures. The recursion depth can go up to `L`, so the call stack uses `O(L)` space. Since `L < n`, this is `O(n)`.
Trade-offs
Pros
Simple to understand and follows the problem definition directly.
Cons
Extremely inefficient due to its exponential time complexity.
Will cause a 'Time Limit Exceeded' error on platforms like LeetCode for the given constraints.
This approach also starts by finding the maximum depth L. Instead of brute-force, it uses dynamic programming to efficiently count the number of ways. We build up the solution for a path of length i based on the solutions for a path of length i-1, keeping track of the number of ways to form even and odd sums.
Algorithm
- Build Graph & Find Max Depth (L): As in the previous approach, use BFS to find the maximum depth
L. - DP State: Define a DP state based on the number of edges processed. Let
evenWays[i]be the number of ways to get an even sum usingiedges, andoddWays[i]be the number of ways to get an odd sum. - DP Initialization: For a single edge (
i=1):evenWays[1] = 1(by assigning weight 2).oddWays[1] = 1(by assigning weight 1).
- DP Recurrence: For
ifrom 2 toL:- To get an even sum for
iedges: (even sum fromi-1+ even weight) OR (odd sum fromi-1+ odd weight). So,evenWays[i] = evenWays[i-1] + oddWays[i-1]. - To get an odd sum for
iedges: (even sum fromi-1+ odd weight) OR (odd sum fromi-1+ even weight). So,oddWays[i] = evenWays[i-1] + oddWays[i-1].
- To get an even sum for
- Space Optimization: Notice that the calculation for
ionly depends oni-1. We can use two variables instead of arrays to store the counts for the previous step, reducing space toO(1)for the DP part. - Result: The answer is
oddWays[L]after iterating up toL.
Walkthrough
After finding the maximum path length L, we can solve the counting problem using dynamic programming. Let odd_ways be the number of ways to get an odd sum and even_ways be the number of ways to get an even sum. We can build the solution iteratively.
For a path of length 1, there's 1 way to get an odd sum (weight 1) and 1 way to get an even sum (weight 2).
For a path of length i, we can extend the paths of length i-1. An odd sum can be formed by taking an even sum and adding 1, or an odd sum and adding 2. Thus, the new number of odd ways is the sum of the old odd and even ways. The same logic applies to forming an new even sum.
class Solution { private long MOD = 1000000007; public int numberOfWays(int[][] edges) { int n = edges.length + 1; if (n <= 1) return 0; List<List<Integer>> adj = new ArrayList<>(); for (int i = 0; i <= n; i++) { adj.add(new ArrayList<>()); } for (int[] edge : edges) { adj.get(edge[0]).add(edge[1]); adj.get(edge[1]).add(edge[0]); } int L = findMaxDepth(n, adj); if (L == 0) return 0; // DP state for path of length 1 long evenWays = 1; // assign weight 2 long oddWays = 1; // assign weight 1 // Iterate from path length 2 to L for (int i = 2; i <= L; i++) { long nextEven = (evenWays + oddWays) % MOD; long nextOdd = (evenWays + oddWays) % MOD; evenWays = nextEven; oddWays = nextOdd; } return (int) oddWays; } private int findMaxDepth(int n, List<List<Integer>> adj) { Queue<int[]> queue = new LinkedList<>(); queue.offer(new int[]{1, 0}); // {node, depth} boolean[] visited = new boolean[n + 1]; visited[1] = true; int maxDepth = 0; while (!queue.isEmpty()) { int[] current = queue.poll(); int u = current[0]; int depth = current[1]; maxDepth = Math.max(maxDepth, depth); for (int v : adj.get(u)) { if (!visited[v]) { visited[v] = true; queue.offer(new int[]{v, depth + 1}); } } } return maxDepth; }}Complexity
Time
O(n + L) = O(n). `O(n)` for finding the max depth `L`. The DP calculation involves a loop that runs `L-1` times, taking `O(L)` time. Since `L < n`, the total complexity is dominated by `O(n)`.
Space
O(n). `O(n)` for the adjacency list and BFS data structures. The DP calculation itself is optimized to use `O(1)` space.
Trade-offs
Pros
Efficient enough to pass the given constraints.
Systematically builds the solution from smaller subproblems.
Cons
The calculation part is linear in
L, which is slightly less optimal than the final approach, though both are dominated by theO(n)graph traversal.
This is the most efficient and elegant approach. It leverages a combinatorial insight to find the answer directly without iteration or recursion on the path length. After finding the max depth L, the problem reduces to a single mathematical calculation which can be performed very quickly.
Algorithm
- Build Graph & Find Max Depth (L): Use BFS to find the maximum depth
Lof the tree, which corresponds to the number of edges in the path. - Combinatorial Insight: Realize that the total path cost is odd if and only if the number of edges with weight 1 (an odd weight) is odd. The problem is thus equivalent to choosing an odd number of edges out of
Lto assign weight 1. - Mathematical Formula: The number of ways to choose an odd number of items from a set of
Lis given by the sumC(L, 1) + C(L, 3) + C(L, 5) + .... This sum is a known combinatorial identity that equals2^(L-1)forL > 0. - Calculation: The number of ways is
2^(L-1). We need to compute this value modulo10^9 + 7. - Modular Exponentiation: Use the efficient 'exponentiation by squaring' algorithm to calculate
pow(2, L-1)under the given modulus. This computes the result in logarithmic time with respect to the exponent.
Walkthrough
The most direct solution comes from a mathematical observation. A sum of integers is odd if and only if it contains an odd number of odd integers. In our case, the weights are 1 (odd) and 2 (even). Therefore, for the total path cost to be odd, we must assign weight 1 to an odd number of edges on the path.
Let the path length be L. The total number of ways to assign weights to L edges is 2^L. For any path of length i, the number of ways to get an odd sum is equal to the number of ways to get an even sum, which is 2^(i-1). This can be proven by induction or by the DP relations from the previous approach. For a path of length L, the number of ways to get an odd sum is 2^(L-1).
We first find L using BFS, and then compute 2^(L-1) modulo 10^9 + 7 using modular exponentiation.
class Solution { private long MOD = 1000000007; public int numberOfWays(int[][] edges) { int n = edges.length + 1; if (n <= 1) return 0; List<List<Integer>> adj = new ArrayList<>(); for (int i = 0; i <= n; i++) { adj.add(new ArrayList<>()); } for (int[] edge : edges) { adj.get(edge[0]).add(edge[1]); adj.get(edge[1]).add(edge[0]); } int L = findMaxDepth(n, adj); if (L == 0) return 0; return (int) power(2, L - 1); } private long power(long base, long exp) { long res = 1; base %= MOD; while (exp > 0) { if (exp % 2 == 1) res = (res * base) % MOD; base = (base * base) % MOD; exp /= 2; } return res; } private int findMaxDepth(int n, List<List<Integer>> adj) { Queue<int[]> queue = new LinkedList<>(); queue.offer(new int[]{1, 0}); // {node, depth} boolean[] visited = new boolean[n + 1]; visited[1] = true; int maxDepth = 0; while (!queue.isEmpty()) { int[] current = queue.poll(); int u = current[0]; int depth = current[1]; maxDepth = Math.max(maxDepth, depth); for (int v : adj.get(u)) { if (!visited[v]) { visited[v] = true; queue.offer(new int[]{v, depth + 1}); } } } return maxDepth; }}Complexity
Time
O(n + log L) = O(n). `O(n)` for finding the max depth `L` via BFS. The modular exponentiation takes `O(log L)` time. The overall complexity is dominated by the `O(n)` graph traversal.
Space
O(n). `O(n)` for the adjacency list and BFS data structures. The modular exponentiation function uses `O(1)` space for the iterative version.
Trade-offs
Pros
Most efficient method, especially the calculation part.
Code is concise and clean.
Cons
Requires recognizing the underlying mathematical pattern or combinatorial identity.
Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.