Hard

题目描述

给你一个长度为 n 的整数数组 receiver 和一个整数 kn 名玩家正在进行传球游戏。

你可以选择起始玩家 i。游戏按以下方式进行:玩家 i 将球传给玩家 receiver[i],然后 receiver[i] 传给 receiver[receiver[i]],依此类推,总共进行 k 次传球。游戏的得分是所有触球玩家的索引之和,包括重复,即 i + receiver[i] + receiver[receiver[i]] + ... + receiver^(k)[i]

返回可能的最大得分。

注意:

  • receiver 可能包含重复元素。
  • receiver[i] 可能等于 i

示例 1:

输入:receiver = [2,0,1], k = 4
输出:6
解释:从玩家 i = 2 开始,初始得分为 2:
- 第1次传球:2 -> 1,得分变为 3
- 第2次传球:1 -> 0,得分变为 3  
- 第3次传球:0 -> 2,得分变为 5
- 第4次传球:2 -> 1,得分变为 6

示例 2:

输入:receiver = [1,1,1,2,3], k = 3
输出:10
解释:从玩家 i = 4 开始,初始得分为 4:
- 第1次传球:4 -> 3,得分变为 7
- 第2次传球:3 -> 2,得分变为 9
- 第3次传球:2 -> 1,得分变为 10

约束:

  • 1 <= receiver.length == n <= 10^5
  • 0 <= receiver[i] <= n - 1
  • 1 <= k <= 10^10

解题思路

这道题的核心挑战是 k 的值可能非常大(最大 10^10),如果直接模拟每次传球会超时。我们需要使用**二进制提升(Binary Lifting)**技术来优化。

解题思路

基本思路: 对于每个起始玩家,我们需要计算经过 k 次传球后的总得分。由于 k 很大,直接模拟不可行。

二进制提升优化:

  1. 预处理每个玩家经过 2^i 次传球后的状态
  2. 对于任意的 k,将其表示为二进制,通过组合不同的 2^i 来快速计算结果

具体实现:

  • next[i][j]:从玩家 i 开始,经过 2^j 次传球后到达的玩家
  • sum[i][j]:从玩家 i 开始,经过 2^j 次传球过程中所有玩家索引的总和

状态转移:

  • 初始状态:next[i][0] = receiver[i]sum[i][0] = receiver[i]
  • 递推关系:
    • next[i][j] = next[next[i][j-1]][j-1]
    • sum[i][j] = sum[i][j-1] + sum[next[i][j-1]][j-1]

计算过程: 对于每个起始玩家,根据 k 的二进制表示,累加对应位为1的贡献,最终得到该起始点的总得分。

时间复杂度主要来自预处理阶段,为 O(n log k),查询阶段为 O(log k)。

代码实现

class Solution {
public:
    long long getMaxFunctionValue(vector<int>& receiver, long long k) {
        int n = receiver.size();
        int maxBit = 64; // log2(10^10) < 64
        
        // next[i][j] = 从i开始经过2^j次传球后到达的玩家
        // sum[i][j] = 从i开始经过2^j次传球过程中的索引和
        vector<vector<int>> next(n, vector<int>(maxBit));
        vector<vector<long long>> sum(n, vector<long long>(maxBit));
        
        // 初始化
        for (int i = 0; i < n; i++) {
            next[i][0] = receiver[i];
            sum[i][0] = receiver[i];
        }
        
        // 二进制提升预处理
        for (int j = 1; j < maxBit; j++) {
            for (int i = 0; i < n; i++) {
                next[i][j] = next[next[i][j-1]][j-1];
                sum[i][j] = sum[i][j-1] + sum[next[i][j-1]][j-1];
            }
        }
        
        long long maxScore = 0;
        
        // 尝试每个起始玩家
        for (int start = 0; start < n; start++) {
            long long score = start; // 起始玩家的索引
            int current = start;
            long long remaining = k;
            
            // 根据k的二进制表示进行跳跃
            for (int bit = 0; bit < maxBit && remaining > 0; bit++) {
                if (remaining & (1LL << bit)) {
                    score += sum[current][bit];
                    current = next[current][bit];
                    remaining -= (1LL << bit);
                }
            }
            
            maxScore = max(maxScore, score);
        }
        
        return maxScore;
    }
};
class Solution:
    def getMaxFunctionValue(self, receiver: List[int], k: int) -> int:
        n = len(receiver)
        max_bit = 64  # log2(10^10) < 64
        
        # next[i][j] = 从i开始经过2^j次传球后到达的玩家
        # sum[i][j] = 从i开始经过2^j次传球过程中的索引和
        next_player = [[0] * max_bit for _ in range(n)]
        sum_indices = [[0] * max_bit for _ in range(n)]
        
        # 初始化
        for i in range(n):
            next_player[i][0] = receiver[i]
            sum_indices[i][0] = receiver[i]
        
        # 二进制提升预处理
        for j in range(1, max_bit):
            for i in range(n):
                next_player[i][j] = next_player[next_player[i][j-1]][j-1]
                sum_indices[i][j] = sum_indices[i][j-1] + sum_indices[next_player[i][j-1]][j-1]
        
        max_score = 0
        
        # 尝试每个起始玩家
        for start in range(n):
            score = start  # 起始玩家的索引
            current = start
            remaining = k
            
            # 根据k的二进制表示进行跳跃
            bit = 0
            while remaining > 0 and bit < max_bit:
                if remaining & (1 << bit):
                    score += sum_indices[current][bit]
                    current = next_player[current][bit]
                    remaining -= (1 << bit)
                bit += 1
            
            max_score = max(max_score, score)
        
        return max_score
public class Solution {
    public long GetMaxFunctionValue(IList<int> receiver, long k) {
        int n = receiver.Count;
        int maxBit = 64; // log2(10^10) < 64
        
        // next[i][j] = 从i开始经过2^j次传球后到达的玩家
        // sum[i][j] = 从i开始经过2^j次传球过程中的索引和
        int[,] next = new int[n, maxBit];
        long[,] sum = new long[n, maxBit];
        
        // 初始化
        for (int i = 0; i < n; i++) {
            next[i, 0] = receiver[i];
            sum[i, 0] = receiver[i];
        }
        
        // 二进制提升预处理
        for (int j = 1; j < maxBit; j++) {
            for (int i = 0; i < n; i++) {
                next[i, j] = next[next[i, j-1], j-1];
                sum[i, j] = sum[i, j-1] + sum[next[i, j-1], j-1];
            }
        }
        
        long maxScore = 0;
        
        // 尝试每个起始玩家
        for (int start = 0; start < n; start++) {
            long score = start; // 起始玩家的索引
            int current = start;
            long remaining = k;
            
            // 根据k的二进制表示进行跳跃
            for (int bit = 0; bit < maxBit && remaining > 0; bit++) {
                if ((remaining & (1L << bit)) != 0) {
                    score += sum[current, bit];
                    current = next[current, bit];
                    remaining -= (1L << bit);
                }
            }
            
            maxScore = Math.Max(maxScore, score);
        }
        
        return maxScore;
    }
}
/**
 * @param {number[]} receiver
 * @param {number} k
 * @return {number}
 */
var getMaxFunctionValue = function(receiver, k) {
    const n = receiver.length;
    const maxBit = 64; // log2(10^10) < 64
    
    // next[i][j] = 从i开始经过2^j次传球后到达的玩家
    // sum[i][j] = 从i开始经过2^j次传球过程中的索引和
    const next = Array(n).fill(null).map(() => Array(maxBit).fill(0));
    const sum = Array(n).fill(null).map(() => Array(maxBit).fill(0));
    
    // 初始化
    for (let i = 0; i < n; i++) {
        next[i][0] = receiver[i];
        sum[i][0] = receiver[i];
    }
    
    // 二进制提升预处理
    for (let j = 1; j < maxBit; j++) {
        for (let i = 0; i < n; i++) {
            next[i][j] = next[next[i][j-1]][j-1];
            sum[i][j] = sum[i][j-1] + sum[next[i][j-1]][j-1];
        }
    }
    
    let maxScore = 0;
    
    // 尝试每个起始玩家
    for (let start = 0; start < n; start++) {
        let score = start; // 起始玩家的索引
        let current = start;
        let remaining = k;
        
        // 根据k的二进制表示进行跳跃
        for (let bit = 0; bit < maxBit && remaining > 0; bit++) {
            if (remaining & (1n << BigInt(bit))) {
                score += sum[current][bit];
                current = next[current][bit];
                remaining -= (1n << BigInt(bit));
            }
        }
        
        maxScore = Math.max(maxScore, score);
    }
    
    return maxScore;
};

复杂度分析

复杂度类型复杂度说明
时间复杂度O(n log k)预处理需要 O(n log k),每个起始点计算需要 O(log k),总共 n 个起始点
空间复杂度O(n log k)需要存储 next 和 sum 两个二维数组,每个大小为 n × log k

相关题目