diff --git a/problems/3558-number-of-ways-to-assign-edge-weights-i/analysis.md b/problems/3558-number-of-ways-to-assign-edge-weights-i/analysis.md new file mode 100644 index 0000000..ae56e68 --- /dev/null +++ b/problems/3558-number-of-ways-to-assign-edge-weights-i/analysis.md @@ -0,0 +1,52 @@ +# 3558. Number of Ways to Assign Edge Weights I + +[LeetCode Link](https://leetcode.com/problems/number-of-ways-to-assign-edge-weights-i/) + +Difficulty: Medium +Topics: Math, Tree, Depth-First Search +Acceptance Rate: 59.4% + +## Hints + +### Hint 1 + +The problem mentions a tree and "maximum depth," but read the question carefully: you only ever care about the *path from the root to a deepest node*. That path is just a chain of edges. So the first job is simply to find how many edges are on that longest root-to-leaf path. Think about how you normally find the depth of a tree. + +### Hint 2 + +Now focus on the counting. Each edge gets weight `1` or `2`. The cost of the path is the sum of those weights, and you want the sum to be **odd**. Notice that `2` is even and `1` is odd. So adding a weight-`2` edge never changes the parity of the running sum — only weight-`1` edges flip it. The parity of the total depends entirely on *how many edges you set to `1`*. + +### Hint 3 + +So for a path with `k` edges, an assignment makes the cost odd exactly when an **odd number** of those edges are set to `1`. The number of subsets of a `k`-element set that have odd size is always `2^(k-1)` (it's exactly half of all `2^k` subsets, as long as `k >= 1`). Therefore the answer is `2^(k-1) mod (1e9 + 7)`, where `k` is the maximum depth (in edges). No DFS over weights is needed — just find `k` and do one fast exponentiation. + +## Approach + +**Step 1 — Find the maximum depth (in edges).** +Build an adjacency list from `edges`. The tree is rooted at node `1`. Run a BFS (or DFS) from node `1`, tracking the distance (number of edges) from the root. The largest distance reached is `k`, the length of the longest root-to-leaf path. BFS is convenient here because each level of the queue corresponds to one more edge of depth, so the number of levels traversed minus one is `k`. + +**Step 2 — Count the odd-cost assignments.** +Along a path of `k` edges, every edge is independently `1` or `2`. The total cost is odd iff an odd number of edges carry weight `1`. The count of length-`k` binary choices with an odd number of "ones" is: + +``` +C(k,1) + C(k,3) + C(k,5) + ... = 2^(k-1) +``` + +This identity holds for every `k >= 1` (half of all `2^k` combinations have odd parity). Since the constraints guarantee `n >= 2`, there is always at least one edge, so `k >= 1` and the formula is well defined. + +Compute `2^(k-1) mod (1e9 + 7)` using fast (binary) exponentiation to keep it efficient and avoid overflow. + +**Why it works — a small example.** +Take `edges = [[1,2],[1,3],[3,4],[3,5]]`. BFS from `1`: depth 0 = {1}, depth 1 = {2,3}, depth 2 = {4,5}. The maximum depth is `k = 2`. The valid assignments on a 2-edge path are `(1,2)` and `(2,1)` — both have exactly one weight-`1` edge (odd count). That's `2^(2-1) = 2`, matching the expected output. + +## Complexity Analysis + +Time Complexity: O(n) — building the adjacency list and the BFS each visit every node and edge once; the fast exponentiation adds O(log k). +Space Complexity: O(n) — for the adjacency list and the BFS queue / visited bookkeeping. + +## Edge Cases + +- **Single edge (`n = 2`, `edges = [[1,2]]`):** Maximum depth `k = 1`, so the answer is `2^0 = 1`. Exactly one of the two weight choices (`1`) is odd. This is the smallest legal input given `n >= 2`. +- **All leaves at the same depth vs. an unbalanced tree:** Only the *maximum* depth matters; siblings or shorter branches are irrelevant. The BFS must track the deepest level, not the first leaf encountered. +- **Large depth (long chain):** With up to `10^5` nodes the path could have ~`10^5` edges, so `2^(k-1)` is astronomically large — you must reduce modulo `1e9 + 7` during the exponentiation, never compute the raw power. +- **Wide, shallow tree (star graph):** Many children of the root but depth `1`; the answer is still `1`. Confirms the count depends on depth, not node count. diff --git a/problems/3558-number-of-ways-to-assign-edge-weights-i/problem.md b/problems/3558-number-of-ways-to-assign-edge-weights-i/problem.md new file mode 100644 index 0000000..7d2e228 --- /dev/null +++ b/problems/3558-number-of-ways-to-assign-edge-weights-i/problem.md @@ -0,0 +1,74 @@ +--- +number: "3558" +frontend_id: "3558" +title: "Number of Ways to Assign Edge Weights I" +slug: "number-of-ways-to-assign-edge-weights-i" +difficulty: "Medium" +topics: + - "Math" + - "Tree" + - "Depth-First Search" +acceptance_rate: 5940.3 +is_premium: false +created_at: "2026-06-11T05:07:20.705087+00:00" +fetched_at: "2026-06-11T05:07:20.705087+00:00" +link: "https://leetcode.com/problems/number-of-ways-to-assign-edge-weights-i/" +date: "2026-06-11" +--- + +# 3558. Number of Ways to Assign Edge Weights I + +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:** + +![](https://assets.leetcode.com/uploads/2025/03/23/screenshot-2025-03-24-at-060006.png) + +**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:** + +![](https://assets.leetcode.com/uploads/2025/03/23/screenshot-2025-03-24-at-055820.png) + +**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 -> 3` and `3 -> 4`). + * Assigning weights (1,2) or (2,1) results in an odd cost. Thus, the number of valid assignments is 2. + + + + + +**Constraints:** + + * `2 <= n <= 105` + * `edges.length == n - 1` + * `edges[i] == [ui, vi]` + * `1 <= ui, vi <= n` + * `edges` represents a valid tree. diff --git a/problems/3558-number-of-ways-to-assign-edge-weights-i/solution_daily_20260611.go b/problems/3558-number-of-ways-to-assign-edge-weights-i/solution_daily_20260611.go new file mode 100644 index 0000000..80a31d4 --- /dev/null +++ b/problems/3558-number-of-ways-to-assign-edge-weights-i/solution_daily_20260611.go @@ -0,0 +1,65 @@ +package main + +// 3558. Number of Ways to Assign Edge Weights I +// +// Approach: +// We only care about the path from the root (node 1) to a node at the maximum +// depth. Every edge on that path is assigned weight 1 (odd) or 2 (even). Since +// weight 2 never changes the parity of the running sum, the total cost is odd +// exactly when an odd number of edges are set to 1. For a path with k edges, +// the number of subsets of odd size is 2^(k-1). So the answer is +// 2^(maxDepth-1) mod (1e9+7), where maxDepth is the number of edges on the +// longest root-to-leaf path. We find maxDepth with a BFS and compute the power +// with fast (binary) exponentiation. + +const mod = 1_000_000_007 + +func assignEdgeWeights(edges [][]int) int { + n := len(edges) + 1 + + // Build adjacency list (nodes are 1-indexed). + adj := make([][]int, n+1) + for _, e := range edges { + u, v := e[0], e[1] + adj[u] = append(adj[u], v) + adj[v] = append(adj[v], u) + } + + // BFS from node 1 to find the maximum depth in edges. + visited := make([]bool, n+1) + queue := []int{1} + visited[1] = true + maxDepth := 0 + for len(queue) > 0 { + next := make([]int, 0, len(queue)) + for _, node := range queue { + for _, nb := range adj[node] { + if !visited[nb] { + visited[nb] = true + next = append(next, nb) + } + } + } + if len(next) > 0 { + maxDepth++ + } + queue = next + } + + // Number of odd-cost assignments on a path of maxDepth edges is 2^(maxDepth-1). + return powMod(2, maxDepth-1, mod) +} + +// powMod computes base^exp mod m using binary exponentiation. +func powMod(base, exp, m int) int { + base %= m + result := 1 + for exp > 0 { + if exp&1 == 1 { + result = result * base % m + } + base = base * base % m + exp >>= 1 + } + return result +} diff --git a/problems/3558-number-of-ways-to-assign-edge-weights-i/solution_daily_20260611_test.go b/problems/3558-number-of-ways-to-assign-edge-weights-i/solution_daily_20260611_test.go new file mode 100644 index 0000000..9fba0d1 --- /dev/null +++ b/problems/3558-number-of-ways-to-assign-edge-weights-i/solution_daily_20260611_test.go @@ -0,0 +1,51 @@ +package main + +import "testing" + +func TestAssignEdgeWeights(t *testing.T) { + tests := []struct { + name string + edges [][]int + expected int + }{ + { + name: "example 1: single edge, depth 1", + edges: [][]int{{1, 2}}, + expected: 1, + }, + { + name: "example 2: max depth 2 with branching", + edges: [][]int{{1, 2}, {1, 3}, {3, 4}, {3, 5}}, + expected: 2, + }, + { + name: "edge case: star graph, all leaves at depth 1", + edges: [][]int{{1, 2}, {1, 3}, {1, 4}, {1, 5}}, + expected: 1, + }, + { + name: "edge case: straight chain of depth 5", + edges: [][]int{{1, 2}, {2, 3}, {3, 4}, {4, 5}, {5, 6}}, + expected: 16, // 2^(5-1) + }, + { + name: "edge case: unbalanced tree, deepest branch wins", + edges: [][]int{{1, 2}, {2, 3}, {1, 4}}, + expected: 2, // max depth 2 -> 2^(2-1) + }, + { + name: "edge case: edges given out of order", + edges: [][]int{{3, 4}, {1, 3}, {1, 2}}, + expected: 2, // path 1->3->4 has depth 2 + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := assignEdgeWeights(tt.edges) + if result != tt.expected { + t.Errorf("assignEdgeWeights(%v) = %d, want %d", tt.edges, result, tt.expected) + } + }) + } +}