求解将k个最短弹簧调整至同长度的最小代价高效算法
将k个最短弹簧调整至同一长度的最小代价最优解法
问题分析
给定已排序的弹簧长度数组x(x₁ ≤ x₂ ≤ ... ≤ xₙ),每个弹簧移动D厘米的代价为1+2+…+D = D*(D+1)/2。对于每个查询k,需计算将前k个最短弹簧调整至同一长度的最小总代价,要求处理n,q ≤ 1e6的大数据量。
核心推导
移动D厘米的代价可展开为:
cost(D) = (D² + D) / 2
总代价为所有弹簧的代价之和,即:
total_cost = (sum(Dᵢ²) + sum(Dᵢ)) / 2
其中Dᵢ = |xᵢ - t|,t为目标长度。要最小化总代价,需同时优化sum(Dᵢ²)和sum(Dᵢ),通过分析可知,最优的t对应前k个元素的中位数附近位置,且可通过前缀和快速计算对应代价。
最优解法步骤
预处理前缀和与前缀平方和
- 前缀和数组
S:S[k] = x₁ + x₂ + ... + xₖ(S[0] = 0) - 前缀平方和数组
Sq:Sq[k] = x₁² + x₂² + ... + xₖ²(Sq[0] = 0)
这两个数组可在O(n)时间内完成计算。
- 前缀和数组
双指针法确定每个k的最优目标位置
对于每个k,我们需要找到前k个元素中使总代价最小的位置m(即目标长度为xₘ)。利用双指针维护m:- 初始化
m = 1 - 遍历
k从1到n:- 若
m < k且调整到xₘ₊₁的代价更小,则m += 1 - 记录当前
m为opt[k](k对应的最优位置)
代价差的符号可通过简化公式判断(避免全量计算),确保每个k的处理均摊为O(1),总时间O(n)。
- 若
- 初始化
预处理每个k的最小代价
对于每个k,利用opt[k]计算最小总代价:- 计算
sum(Dᵢ²) = Sq[k] - 2*xₘ*S[k] + k*xₘ² - 计算
sum(Dᵢ) = xₘ*(2m - k) + S[k] - 2*S[m] - 总代价为
(sum(Dᵢ²) + sum(Dᵢ)) * inv2 % MOD,其中inv2 = 500000004(模1e9+7下2的逆元)
所有运算需在模1e9+7下进行,注意处理负数(加模后再取模),同时使用64位整数避免溢出。
- 计算
处理查询
每个查询k直接返回预处理好的ans[k],时间O(1),总查询时间O(q)。
关键注意事项
- 由于
xᵢ可达1e9,计算xₘ*xₘ时需用64位整数(如C++中的long long),防止溢出后再取模。 - 模运算中除法需用逆元替代,
2的逆元在1e9+7下为500000004。 - 双指针法确保了最优位置
m的单调递增,保证了预处理的线性时间复杂度。
内容的提问来源于stack exchange,提问作者randomAlgo
相关产品推荐
相关产品推荐

