tree_of_space_problem.md

๐ŸŒ Tree of Space โ€” Hackathon Problem Statement

Difficulty: Hard | Category: Trees, Data Structures, Concurrency This is the actual Juspay Hackathon problem (Part A & Part B), reconstructed with full constraints, edge cases, and test cases.


๐Ÿ“– Background

You are given a world map modeled as a Generic Balanced M-ary Tree with N nodes. Each node represents a geographic region. You must build a lock management system on this tree that supports three operations: Lock, Unlock, and UpgradeLock.

The tree is given in level-order (BFS order) โ€” the root is the first node, followed by its children left to right, then their children left to right, and so on. Each internal node has at most M children.


๐Ÿ“ฅ Input Format

N M Q node_1 node_2 node_3 ... node_N type_1 node_name_1 uid_1 type_2 node_name_2 uid_2 ... type_Q node_name_Q uid_Q
  • Line 1: Three integers N (nodes), M (max children per node), Q (queries)
  • Line 2: N space-separated unique strings โ€” node names in level-order (BFS)
  • Next Q lines: Each query has:
    • type : 1 = Lock, 2 = Unlock, 3 = UpgradeLock
    • node_name : Name of the target node (guaranteed to exist)
    • uid : Integer user ID

๐Ÿ“ค Output Format

For each of the Q queries, print true or false on a new line.



๐Ÿ”’ Part A โ€” Lock and Unlock

Operations

1. Lock(X, uid) โ†’ type 1

Lock node X for user uid, granting exclusive access to the entire subtree rooted at X.

Returns true if ALL of the following hold:

  1. Node X is not already locked (by any user)
  2. No ancestor of X is locked (by any user)
  3. No descendant of X is locked (by any user)

Returns false otherwise.

If successful, node X is marked as locked by uid.


2. Unlock(X, uid) โ†’ type 2

Unlock node X that was locked by uid.

Returns true if ALL of the following hold:

  1. Node X is currently locked
  2. Node X is locked by the same uid making the request

Returns false otherwise.

If successful, node X is unlocked and is now available for future operations.


๐ŸŒฒ Tree Structure Visualization

For the example: N=7, M=2, nodes = [World, Asia, Africa, China, India, SouthAfrica, Egypt]

World โ† Level 0 (index 1) / \ Asia Africa โ† Level 1 (index 2, 3) / \ / \ China India SouthAfrica Egypt โ† Level 2 (index 4,5,6,7)

M-ary Index Formula (1-based):

  • Parent of node at index i: (i - 2) / M + 1
  • Children of node at index i: M*(i-1)+2 to M*(i-1)+M+1

๐Ÿงช Test Cases โ€” Part A

Test Case A-1 (Basic Lock/Unlock)

Input: 7 2 6 World Asia Africa China India SouthAfrica Egypt 1 China 9 1 Asia 2 2 China 9 1 Asia 2 1 World 3 1 Egypt 4 Expected Output: true false true true false true

Explanation:

#OperationResultReason
1Lock(China, 9)trueChina is free, no locked ancestors/descendants
2Lock(Asia, 2)falseChina (descendant of Asia) is locked
3Unlock(China, 9)trueChina is locked by uid 9 โ€” same uid
4Lock(Asia, 2)trueNow no locked ancestors/descendants
5Lock(World, 3)falseAsia (descendant of World) is locked
6Lock(Egypt, 4)trueEgypt is free; Africa (ancestor) is not locked (Asia is locked under World, but Egypt is under Africa)

Test Case A-2 (Ancestor Check)

Input: 7 2 5 World Asia Africa China India SouthAfrica Egypt 1 World 1 1 China 2 1 Asia 3 2 World 1 1 Asia 3 Expected Output: true false false true true

Explanation:

#OperationResultReason
1Lock(World, 1)trueRoot is free
2Lock(China, 2)falseWorld (ancestor of China) is locked
3Lock(Asia, 3)falseWorld (ancestor of Asia) is locked
4Unlock(World, 1)trueWorld is locked by uid=1
5Lock(Asia, 3)trueWorld is now free

Test Case A-3 (Already Locked Node)

Input: 3 2 4 World China India 1 World 5 1 World 5 2 World 7 2 World 5 Expected Output: true false false true

Explanation:

#OperationResultReason
1Lock(World, 5)trueWorld is free
2Lock(World, 5)falseWorld is already locked
3Unlock(World, 7)falseWorld is locked by uid=5, not uid=7
4Unlock(World, 5)trueCorrect uid

Test Case A-4 (Sibling Nodes โ€” No Conflict)

Input: 7 2 4 World Asia Africa China India SouthAfrica Egypt 1 China 1 1 India 2 1 SouthAfrica 3 1 Egypt 4 Expected Output: true true true true

Explanation:
All four nodes are leaf siblings (or under separate branches). None are ancestors/descendants of each other, so all locks succeed independently.


Test Case A-5 (Interleaved Lock/Unlock)

Input: 10 3 8 Root A B C D E F G H I 1 D 1 1 E 2 2 D 1 1 A 3 1 E 2 2 E 2 1 A 3 2 A 3 Expected Output: true true true false false true true true

Tree structure for N=10, M=3:

Root / | \ A B C /|\ D E F

Explanation:

#OperationResultReason
1Lock(D, 1)trueD is free
2Lock(E, 2)trueE is free, D is sibling
3Unlock(D, 1)trueD locked by uid=1
4Lock(A, 3)falseE (descendant) is still locked
5Lock(E, 2)falseE is already locked
6Unlock(E, 2)trueE locked by uid=2
7Lock(A, 3)trueNo locked ancestors or descendants
8Unlock(A, 3)trueA locked by uid=3

Test Case A-6 (Edge โ€” Single Node Tree)

Input: 1 2 3 Root 1 Root 42 2 Root 42 2 Root 42 Expected Output: true true false


โฌ†๏ธ Part B โ€” Upgrade Lock

New Operation

3. UpgradeLock(X, uid) โ†’ type 3

Consolidates multiple fine-grained descendant locks into a single lock on an ancestor node X.

Returns true if ALL of the following hold:

  1. Node X is not currently locked
  2. No ancestor of X is locked (by any user)
  3. X has at least one locked descendant
  4. ALL locked descendants of X are locked by exactly the same uid โ€” if even one is locked by a different user, the upgrade fails

If successful:

  • All locked descendants of X are automatically unlocked
  • Node X is locked by uid

Key insight: The ancestor check in Upgrade can be implicitly guaranteed if you can confirm that at least one descendant got locked successfully (because a lock can only succeed if no ancestor is locked). However, an explicit ancestor check is safer.


๐Ÿงช Test Cases โ€” Part B

Test Case B-1 (Official Example)

Input: 7 2 5 World Asia Africa China India SouthAfrica Egypt 1 China 9 1 India 9 3 Asia 9 2 India 9 2 Asia 9 Expected Output: true true true false true

Explanation:

#OperationResultReason
1Lock(China, 9)trueChina is free
2Lock(India, 9)trueIndia is free, sibling of China
3Upgrade(Asia, 9)trueAsia is unlocked, descendants [China, India] both locked by uid=9. Asia gets locked; China and India are unlocked.
4Unlock(India, 9)falseIndia was unlocked during Upgrade
5Unlock(Asia, 9)trueAsia is now locked by uid=9

Test Case B-2 (Upgrade Fails โ€” Mixed UIDs)

Input: 7 2 4 World Asia Africa China India SouthAfrica Egypt 1 China 1 1 India 2 3 Asia 1 3 Asia 2 Expected Output: true true false false

Explanation:

#OperationResultReason
1Lock(China, 1)trueFree
2Lock(India, 2)trueFree, sibling
3Upgrade(Asia, 1)falseIndia is locked by uid=2, not uid=1. Mixed UIDs โ†’ fails
4Upgrade(Asia, 2)falseChina is locked by uid=1, not uid=2. Still mixed โ†’ fails

Test Case B-3 (Official Example 2)

Input: 3 2 2 World China India 3 India 1 1 World 9 Expected Output: false true

Explanation:

#OperationResultReason
1Upgrade(India, 1)falseIndia is a leaf node โ€” it has no descendants, so there are zero locked descendants. Upgrade requires at least one.
2Lock(World, 9)trueWorld is free

Test Case B-4 (Upgrade then attempt to lock upgraded ancestor's ancestor)

Input: 7 2 5 World Asia Africa China India SouthAfrica Egypt 1 China 5 1 India 5 3 Asia 5 1 World 5 2 Asia 5 Expected Output: true true true false true

Explanation:

#OperationResultReason
1Lock(China, 5)true
2Lock(India, 5)true
3Upgrade(Asia, 5)trueAsia is now locked; China, India freed
4Lock(World, 5)falseAsia (descendant of World) is now locked
5Unlock(Asia, 5)true

Test Case B-5 (Upgrade with Locked Ancestor โ†’ Fails)

Input: 7 2 5 World Asia Africa China India SouthAfrica Egypt 1 World 1 1 China 1 3 Asia 1 2 World 1 3 Asia 1 Expected Output: true false false true false

Explanation:

#OperationResultReason
1Lock(World, 1)trueRoot locked
2Lock(China, 1)falseWorld (ancestor) is locked
3Upgrade(Asia, 1)falseNo locked descendants (China lock failed)
4Unlock(World, 1)true
5Upgrade(Asia, 1)falseStill no locked descendants

Test Case B-6 (Chained Upgrade & Descendant Checks)

Input: 7 2 4 World Asia Africa China India SouthAfrica Egypt 1 China 3 1 Asia 3 3 Asia 3 3 World 3 Expected Output: true false true true

Explanation:

#OperationResultReason
1Lock(China, 3)trueChina is free, no locked ancestors/descendants.
2Lock(Asia, 3)falseNormal Lock fails because descendant China is locked.
3Upgrade(Asia, 3)trueAsia is unlocked; only descendant China is locked (by uid=3). Asia gets locked; China unlocked.
4Upgrade(World, 3)trueWorld is unlocked; descendant Asia is locked (by uid=3). World gets locked; Asia unlocked.

Test Case B-6b (Upgrade on Already-Locked Node Fails)

Input: 7 2 3 World Asia Africa China India SouthAfrica Egypt 1 Asia 3 3 Asia 3 3 World 3 Expected Output: true false true

Explanation:

#OperationResultReason
1Lock(Asia, 3)trueAsia is free. Asia gets locked by uid=3.
2Upgrade(Asia, 3)falseAsia is already locked โ€” Upgrade requires the target node to be unlocked.
3Upgrade(World, 3)trueWorld is unlocked; only descendant Asia is locked (by uid=3). World gets locked; Asia unlocked.

Test Case B-7 (Large M-ary, complex scenario)

Input: 10 3 7 Root A B C D E F G H I 1 D 7 1 E 7 1 F 7 3 A 7 1 B 9 2 Root 7 1 Root 7 Expected Output: true true true true true false false

Tree: N=10, M=3 โ†’ Root has children [A,B,C], A has children [D,E,F], B has children [G,H,I]

Root / | \ A B C / | \ /|\ D E F G H I

Explanation:

#OperationResultReason
1Lock(D, 7)trueD is free
2Lock(E, 7)trueSibling of D
3Lock(F, 7)trueSibling of D,E
4Upgrade(A, 7)trueA is unlocked, all descendants [D,E,F] locked by uid=7. A gets locked; D,E,F freed.
5Lock(B, 9)trueB is free โ€” A's lock doesn't affect B
6Unlock(Root, 7)falseRoot is NOT locked
7Lock(Root, 7)falseA (descendant, locked) and B (descendant, locked) block this

โš ๏ธ Critical Edge Cases to Remember

  1. Upgrade on leaf node โ†’ Always false (no descendants)
  2. Upgrade on already-locked node โ†’ Always false
  3. Lock on node with locked ancestor โ†’ false, even if no descendants locked
  4. Unlock with wrong uid โ†’ false
  5. After Upgrade(X), all descendants of X are unlocked โ€” subsequent Unlock on those descendants will return false
  6. Sibling nodes can both be locked (neither is ancestor/descendant of the other)
  7. Upgrade checks ancestor lock โ€” even if all descendants are by same uid, if an ancestor is locked, Upgrade fails

๐Ÿ“Š Complexity Requirements

OperationTime ComplexityKey Optimization
LockO(log_M N)Store lockedDescendants set in each node; check ancestors by walking up
UnlockO(log_M N)Walk up to root, remove self from each ancestor's lockedDescendants
UpgradeO(K ยท log_M N)K = number of locked descendants; unlock each in O(log_M N)
SpaceO(N)One set per node

Why O(log_M N) for ancestor traversal?
In a balanced M-ary tree with N nodes, the height is log_M(N). Walking from any node to root = O(height) = O(log_M N).


๐Ÿ”ง Key Data Structures

struct TreeNode { string name; int lockedBy; // -1 if not locked bool isLocked; vector<TreeNode*> children; TreeNode* parent; set<TreeNode*> lockedDescendants; // CRITICAL for O(1) descendant check };

Why lockedDescendants is the key insight:

  • Without it: checking descendants = O(N) BFS every time
  • With it: checking lockedDescendants.size() == 0 is O(1)
  • Maintained by calling updateParents() on every lock/unlock

๐Ÿ“‹ Summary of Constraints

ConstraintRule
Lock a locked nodeโŒ false
Lock when ancestor is lockedโŒ false
Lock when any descendant is lockedโŒ false
Unlock an unlocked nodeโŒ false
Unlock with wrong uidโŒ false
Upgrade when node is lockedโŒ false
Upgrade when ancestor is lockedโŒ false
Upgrade with zero locked descendantsโŒ false
Upgrade when any descendant locked by different uidโŒ false
Upgrade success โ†’ descendants auto-unlockโœ… yes