DAG Downstream Nodes
Reported by candidates from Notion's online assessment. Pattern, common pitfall, and the honest play if you blank under the timer.
The Notion OA reported in April 2026 hands you adjacency rows like ["A","B","C"] and asks for DIRECT and ALL downstream queries on a DAG. It looks like a graph problem, and it is, but it's really a parsing and formatting problem with a traversal bolted on. The first value in each row is the node, the rest are its children. Build a map, run a DFS or BFS for ALL, sort, print. If you've done a reachability question before, this is the same shape. The traps are small and they cost points.
The problem
A directed acyclic graph is encoded by adjacency rows. The first value in each row is a node and the remaining values are its direct downstream nodes. Each query is either DIRECT node or ALL node. A direct query returns that node's direct downstream nodes. An all query returns every distinct node reachable by one or more edges. Sort every answer lexicographically and format it as [node1,node2]; return [] when the answer is empty. Return one formatted answer for every query in order. Function queryDownstream(adjacency: String[][], queries: String[]) → String[] Examples Example 1 adjacency = [["A","B","C"],["B","D"],["C","D","E"],["D"],["E"]] queries = ["DIRECT A","ALL A","DIRECT D","ALL C"] return = ["[B,C]","[B,C,D,E]","[]","[D,E]"] A directly reaches B and C. Its transitive result includes those nodes plus D and E once, even though D has two incoming paths. Constraints 1 <= adjacency.length <= 100000. Every node has exactly one adjacency row, including nodes with no outgoing edges. Node IDs are unique non-empty ASCII strings containing neither a comma nor whitespace. The graph has no self-edge, duplicate edge, or directed cycle. 1 <= queries.length <= 100000, and every queried node exists. The total number of nodes visited across all ALL queries is at most 300000.
Reported by candidates. Source: FastPrep
Pattern and pitfall
Pattern: depth-first search over a hash map of node to children. Parse each row once into a map. DIRECT is a lookup, copy, and sort. ALL is a traversal from the queried node with a visited set, so a node like D that has two incoming paths is counted once. Don't include the start node in its own answer. The constraint says total visited nodes across ALL queries is at most 300000, so a fresh traversal per query is fine and you don't need memoization. Use an iterative stack, since 100000 nodes in a chain can blow a recursive call stack. Sort lexicographically as strings, not numerically, then join with commas, no spaces, wrapped in brackets. Empty gives []. If you blank on the formatting or the recursion limit during the live OA, StealthCoder is the invisible safety net that gives you a working solution on screen.
Memorize the pattern. If you can't, run StealthCoder. The proctor sees the IDE. They don't see what's behind it.
You can drill DAG Downstream Nodes cold, or you can hedge it. StealthCoder runs invisibly during screen share and surfaces a working solution in under 2 seconds. The proctor sees the IDE. They don't see what's behind it. Made by an engineer who treats the OA as theater. If yours is tonight, you don't have time to grind. You have time to hedge.
Get StealthCoderRelated leaked OAs
You've seen the question.
Make sure you actually pass Notion's OA.
Notion reuses patterns across OAs. Made by an engineer who treats the OA as theater. If yours is tonight, you don't have time to grind. You have time to hedge. Works on HackerRank, CodeSignal, CoderPad, and Karat.
DAG Downstream Nodes FAQ
What's the trick in the Notion DAG Downstream Nodes problem?+
Build a map from node to its children, then run a traversal with a visited set for ALL queries. The visited set handles nodes reachable by multiple paths so each appears once. DIRECT is just a sorted copy of the child list.
Do I need memoization or topological sort here?+
No. The constraints cap total visited nodes across all ALL queries at 300000, so a plain DFS or BFS per query is fast enough. Memoizing reachable sets could actually use far more memory on a large graph, so skip it.
Should I use recursion or an iterative DFS?+
Go iterative with an explicit stack. The graph can have 100000 nodes, and a long chain would overflow the recursion limit in many languages. BFS with a queue works equally well since order doesn't matter before the sort.
What output formatting mistakes cost people on this one?+
Spaces after commas, forgetting [] for empty results, and sorting the wrong way. Join with a bare comma, wrap in brackets, and sort as plain strings. Also don't include the queried node itself in its own ALL result.
How do I prepare for this in 48 hours?+
Write a reachability DFS on an adjacency map from scratch, twice. Then practice parsing rows where the first element is the key. Add a quick check for sorted output and empty results. That covers everything this question tests.