Maximize Value of Function in a Ball Passing Game
HardPrompt
You are given an integer array receiver of length n and an integer k. n players are playing a ball-passing game.
You choose the starting player, i. The game proceeds as follows: player i passes the ball to player receiver[i], who then passes it to receiver[receiver[i]], and so on, for k passes in total. The game's score is the sum of the indices of the players who touched the ball, including repetitions, i.e. i + receiver[i] + receiver[receiver[i]] + ... + receiver(k)[i].
Return the maximum possible score.
Notes:
receivermay contain duplicates.receiver[i]may be equal toi.
Example 1:
Input: receiver = [2,0,1], k = 4
Output: 6
Explanation:
Starting with player i = 2 the initial score is 2:
| Pass | Sender Index | Receiver Index | Score |
|---|---|---|---|
| 1 | 2 | 1 | 3 |
| 2 | 1 | 0 | 3 |
| 3 | 0 | 2 | 5 |
| 4 | 2 | 1 | 6 |
Example 2:
Input: receiver = [1,1,1,2,3], k = 3
Output: 10
Explanation:
Starting with player i = 4 the initial score is 4:
| Pass | Sender Index | Receiver Index | Score |
|---|---|---|---|
| 1 | 4 | 3 | 7 |
| 2 | 3 | 2 | 9 |
| 3 | 2 | 1 | 10 |
Constraints:
1 <= receiver.length == n <= 1050 <= receiver[i] <= n - 11 <= k <= 1010
Approaches
2 approaches with complexity analysis and trade-offs.
This approach directly simulates the ball-passing game for each possible starting player. We iterate through all n players, considering each one as a potential starting point. For each starting player, we simulate k passes, accumulating the score along the way by adding the index of each player who touches the ball.
Algorithm
- Initialize a variable
max_scoreto 0. - Loop through each player
ifrom0ton-1to consider them as the starting player. - For each starting player
i, initializecurrent_score = iandcurrent_player = i. - Loop
ktimes to simulate the passes:- Update
current_playertoreceiver[current_player]. - Add the new
current_player's index tocurrent_score.
- Update
- After the inner loop,
current_scoreholds the total score for starting with playeri. Updatemax_score = max(max_score, current_score). - After iterating through all possible starting players,
max_scorewill hold the maximum possible score.
Walkthrough
The algorithm iterates through every possible starting player from 0 to n-1. For each start player, it simulates the game for k passes. In each pass, it finds the next player using the receiver array and adds their index to a running total for the current game. This process is repeated k times. The maximum score found across all possible starting players is then returned.
class Solution { public long getMaxFunctionValue(int[] receiver, long k) { int n = receiver.length; long maxScore = 0; for (int i = 0; i < n; i++) { long currentScore = i; int currentPlayer = i; for (long j = 0; j < k; j++) { currentPlayer = receiver[currentPlayer]; currentScore += currentPlayer; } maxScore = Math.max(maxScore, currentScore); } return maxScore; }}Complexity
Time
O(n * k). The outer loop runs `n` times, and for each iteration, the inner loop runs `k` times. Given `n` up to `10^5` and `k` up to `10^10`, this is computationally infeasible.
Space
O(1), as we only use a few variables to store the current state, not counting the input array.
Trade-offs
Pros
Simple to understand and implement.
Requires minimal memory.
Cons
Extremely inefficient for large values of
k.Will result in a 'Time Limit Exceeded' (TLE) error on most platforms for the given constraints.
The brute-force approach is too slow because k can be very large. We can optimize this by precomputing information about paths of lengths that are powers of two. This technique is known as binary lifting or sparse table. The core idea is to break down the long path of k passes into segments whose lengths are powers of two. After an O(n log k) precomputation, we can find the destination and the sum of any path of length k in O(log k) time.
Algorithm
- Precomputation:
- Determine the maximum power of two needed,
m, which is approximatelylog2(k). - Create two 2D arrays:
nextPlayer[n][m]to store the destination after2^jsteps, andpathSum[n][m]to store the sum of indices over a path of2^jsteps. - Initialize the base cases for
j=0(path of length2^0 = 1):nextPlayer[i][0] = receiver[i]pathSum[i][0] = i
- Use dynamic programming to fill the tables for
jfrom1tom-1:nextPlayer[i][j] = nextPlayer[nextPlayer[i][j-1]][j-1]pathSum[i][j] = pathSum[i][j-1] + pathSum[nextPlayer[i][j-1]][j-1]
- Determine the maximum power of two needed,
- Querying:
- Initialize
max_score = 0. - For each player
ifrom0ton-1:- Calculate the score for a path of length
k+1(forkpasses). - Use the binary representation of
k+1to assemble the path from the precomputed segments. - Iterate
jfromm-1down to0. If thej-th bit ofk+1is set, add the correspondingpathSumand advance thecurrentPlayer. - Update
max_scorewith the calculated score for starting playeri.
- Calculate the score for a path of length
- Initialize
- Return
max_score.
Walkthrough
We use dynamic programming to precompute jumps of powers of two. We create two tables:
nextPlayer[i][j]: stores the player you reach after2^jpasses starting from playeri.pathSum[i][j]: stores the sum of player indices on a path of2^jnodes, starting from playeri.
The recurrence relations are:
nextPlayer[i][j] = nextPlayer[nextPlayer[i][j-1]][j-1](A jump of2^jis two jumps of2^(j-1)).pathSum[i][j] = pathSum[i][j-1] + pathSum[nextPlayer[i][j-1]][j-1](The sum for a2^jpath is the sum of the first2^(j-1)path segment plus the sum of the next2^(j-1)path segment).
After precomputing these tables, we can answer the query for any starting player i and any number of passes k. The total score is the sum of indices of k+1 players. We find this sum by decomposing k+1 into its binary representation and summing up the precomputed pathSum values for the corresponding powers of two.
class Solution { public long getMaxFunctionValue(int[] receiver, long k) { int n = receiver.length; // The number of bits needed to represent k. Add 1 for safety. int m = (int) (Math.log(k) / Math.log(2)) + 2; // nextPlayer[i][j]: player after 2^j steps starting from i int[][] nextPlayer = new int[n][m]; // pathSum[i][j]: sum of path of length 2^j starting from i long[][] pathSum = new long[n][m]; // Base cases (j=0, i.e., 2^0 = 1 node path) for (int i = 0; i < n; i++) { nextPlayer[i][0] = receiver[i]; pathSum[i][0] = i; } // Precomputation using DP for (int j = 1; j < m; j++) { for (int i = 0; i < n; i++) { int prevHalfEndNode = nextPlayer[i][j - 1]; nextPlayer[i][j] = nextPlayer[prevHalfEndNode][j - 1]; pathSum[i][j] = pathSum[i][j - 1] + pathSum[prevHalfEndNode][j - 1]; } } long maxScore = 0; // The total number of players in the sum is k+1 (k passes + 1 start player) long pathLength = k + 1; for (int i = 0; i < n; i++) { long currentScore = 0; int currentPlayer = i; long len = pathLength; for (int j = m - 1; j >= 0; j--) { if ((len >> j & 1) == 1) { currentScore += pathSum[currentPlayer][j]; currentPlayer = nextPlayer[currentPlayer][j]; } } maxScore = Math.max(maxScore, currentScore); } return maxScore; }}Complexity
Time
O(n * log k). The precomputation takes `O(n * log k)`. The query part iterates through `n` starting nodes, and each query takes `O(log k)`. The total time is dominated by these two parts.
Space
O(n * log k). We use two 2D arrays of size `n x m`, where `m` is approximately `log2(k)`.
Trade-offs
Pros
Highly efficient for large
k, making it feasible for the given constraints.It's a standard and powerful technique for path-related problems on functional graphs or trees.
Cons
Uses significantly more memory, O(n * log k), compared to the brute-force approach.
The implementation is more complex and less intuitive than a direct simulation.
Solutions
Solution
class Solution {public long getMaxFunctionValue(List<Integer> receiver, long k) { int n = receiver.size(), m = 64 - Long.numberOfLeadingZeros(k); int[][] f = new int[n][m]; long[][] g = new long[n][m]; for (int i = 0; i < n; ++i) { f[i][0] = receiver.get(i); g[i][0] = i; } for (int j = 1; j < m; ++j) { for (int i = 0; i < n; ++i) { f[i][j] = f[f[i][j - 1]][j - 1]; g[i][j] = g[i][j - 1] + g[f[i][j - 1]][j - 1]; } } long ans = 0; for (int i = 0; i < n; ++i) { int p = i; long t = 0; for (int j = 0; j < m; ++j) { if ((k >> j & 1) == 1) { t += g[p][j]; p = f[p][j]; } } ans = Math.max(ans, p + t); } return ans; }}class Solution {public: long long getMaxFunctionValue(vector<int> &receiver, long long k) { int n = receiver.size(), m = 64 - __builtin_clzll(k); int f[n][m]; long long g[n][m]; for (int i = 0; i < n; ++i) { f[i][0] = receiver[i]; g[i][0] = i; } for (int j = 1; j < m; ++j) { for (int i = 0; i < n; ++i) { f[i][j] = f[f[i][j - 1]][j - 1]; g[i][j] = g[i][j - 1] + g[f[i][j - 1]][j - 1]; } } long long ans = 0; for (int i = 0; i < n; ++i) { int p = i; long long t = 0; for (int j = 0; j < m; ++j) { if (k >> j & 1) { t += g[p][j]; p = f[p][j]; } } ans = max(ans, p + t); } return ans; }};class Solution: def getMaxFunctionValue(self, receiver: List[int], k: int) -> int: n, m = len(receiver), k . bit_length() f = [[0] * m for _ in range(n)] g = [[0] * m for _ in range(n)] for i, x in enumerate(receiver): f[i][0] = x g[i][0] = i for j in range(1, m): for i in range(n): f[i][j] = f[f[i][j - 1]][j - 1] g[i][j] = g[i][j - 1] + g[f[i][j - 1]][j - 1] ans = 0 for i in range(n): p, t = i, 0 for j in range(m): if k >> j & 1: t += g[p][j] p = f[p][j] ans = max(ans, t + p) return ansVideo 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.