前两天跟大家聊了京东给外卖骑手缴纳五险一金的事情。文章发出去后,有人留言说:“道理我都懂,但骑手们还是觉得交了社保,到手钱少了啊!”这话戳中了本质——大家不是反对社保本身,而是希望“到手更高”。
社保 VS 到手,谁才是“真香”?
骑手的账本:短期 VS 长期
想象一下,你是个外卖骑手,每天风里来雨里去,一个月赚 8000 块。平台说:“来,交个社保,每月扣 1000,未来有保障。”你掐指一算:
- 不交社保:8000 到手,爽!
- 交社保:7000 到手,嗯?
短期看,少 1000 块,谁不心疼?尤其对骑手这种高强度、快节奏的职业,今天的饭钱都得盘算,更别提“未来养老”这种遥远的事了。前文里提到过,骑手流动性高,很多人干几个月就跳槽,社保的“长期收益”对他们来说像空气——看得见,摸不着。
平台的算盘:合规 VS 成本
平台呢?一边要响应政策,推“新就业形态”从业者参保;一边也怕骑手跑路,毕竟社保成本部分转嫁到骑手身上,谁愿意干?结果就是:骑手觉得“到手变少”,平台觉得“合规真难”。两边都觉得自己吃了亏,典型的“双输博弈”。
延迟退休:交了也白交?
更别提最近热议的“延迟退休”了。骑手们一听:“啥?我得多干几年才能领养老金?”这就像交了房租,结果房东说你得晚几年才能住进去。骑手本来就指望社保“早点回本”,现在延迟退休一出,回报周期拉得更长,吸引力直接腰斩。不少人心里嘀咕:“我跑单都跑不动了,还交啥社保?”
心理博弈:即时满足感胜出
归根结底,骑手抵触社保,是一种“即时满足感”的选择。就像点外卖,你会选“30 分钟送达”还是“明天送达但送杯奶茶”?大多数人选前者。骑手也是如此:到手多 1000 块,能多吃几顿烧烤,社保的“未来回报”太虚幻,抵不过眼前的鸡腿。
破局:到手更高,怎么实现?
1. 骑手想要啥?
骑手不是傻子,他们抵触社保不是因为不懂,而是因为“性价比”不够高。想要他们点头,得让“到手感”更强,比如:
- 降低个人负担:政府或平台多补贴点,别全压在骑手头上。
- 灵活机制:按需参保,比如干满半年再强制交,短期骑手可选“轻量版”保障。
- 即时激励:交社保送补贴,比如每月返 200 块现金,短期也能尝到甜头。
2. 平台的责任
平台别光喊口号,得拿出真金白银。前文说过,平台在“合规”和“留人”间左右为难,但骑手跑了,谁送外卖?不如把社保包装成“福利”,而不是“负担”。比如,美团可以试试“交社保送电动车补贴”,骑手一看:嘿,这买卖划算!
3. 政策的推手
政策得接地气,别一刀切。给骑手群体设计个“新就业保险”,保费低、周期短、回报快,可能比硬推社保更受欢迎。
…
回归主题,我们来一道力扣题目。3428. 最多 K 个元素的子序列的最值之和。
3428. 最多 K 个元素的子序列的最值之和
题目描述
给你一个整数数组 nums
和一个整数 k
,请你返回一个整数,表示从数组中选取 至多 k 个子序列,所有可能方案中,子序列的 最大值之和 加上 最小值之和 的结果。由于结果可能很大,请返回对 (10^9 + 7) 取模后的值。
一个数组的 子序列 是指通过删除一些(可以是 0 个)元素后剩下的序列,且不改变其余元素的相对顺序。例如,[1, 3]
是 [1, 2, 3]
的子序列,而 [2, 1]
不是。
示例 1:
1 | 输入:nums = [1,2,3], k = 2 |
示例 2:
1 | 输入:nums = [2,2], k = 3 |
提示:
- (1 \leq nums.length \leq 10^5)
- (1 \leq nums[i] \leq 10^9)
- (1 \leq k \leq 10^5)
前置知识
- 组合数学:组合数 (C(n, m)) 表示从 (n) 个元素中选 (m) 个的方案数。
- 贡献法
思路
这道题要求计算所有至多 (k) 个子序列的最大值之和与最小值之和。数组的顺序对每个元素的贡献没有任何影响,因此我们可以先对数组进行排序,然后计算每个元素作为最大值或最小值的贡献。
我们可以从贡献的角度来思考:对于数组中的每个元素,它在所有可能的子序列中作为最大值或最小值的次数是多少?然后将这些次数乘以元素值,累加起来即可。
分析
子序列的性质:
- 一个子序列的最大值是其中最大的元素,最小值是最小的元素。
- 对于一个有序数组 (nums),若元素 (nums[i]) 是子序列的最大值,则子序列只能从 (nums[0]) 到 (nums[i]) 中选取,且必须包含 (nums[i])。
- 若 (nums[i]) 是子序列的最小值,则子序列只能从 (nums[i]) 到 (nums[n-1]) 中选取,且必须包含 (nums[i])。
组合计数:
- 假设数组已排序(从小到大),对于 (nums[i]):
- 作为最大值的子序列:从前 (i) 个元素中选 (j) 个((0 \leq j < \min(k, i+1))),再加上 (nums[i]),总方案数为 (\sum_{j=0}^{\min(k, i)} C(i, j))。
- 作为最小值的子序列:从后 (n-i-1) 个元素中选 (j) 个((0 \leq j < \min(k, n-i))),再加上 (nums[i]),总方案数为 (\sum_{j=0}^{\min(k, n-i-1)} C(n-i-1, j))。
- 这里 (C(n, m)) 表示组合数,即从 (n) 个元素中选 (m) 个的方案数。
- 假设数组已排序(从小到大),对于 (nums[i]):
优化组合计算:
- 由于 (n) 和 (k) 可达 (10^5),直接用 (math.comb) 会超时,且需要取模。
- 使用预计算阶乘和逆元的方法,快速计算 (C(n, m) = n! / (m! \cdot (n-m)!) \mod (10^9 + 7))。
最终公式:
- 对每个 (nums[i]),计算其作为最大值的贡献和最小值的贡献,累加后取模。
步骤
- 对数组 (nums) 排序。
- 预计算阶乘 (fac[i]) 和逆元 (inv_f[i])。
- 遍历 (nums):
- 计算 (nums[i]) 作为最大值的次数,乘以 (nums[i]),加到答案中。
- 计算 (nums[i]) 作为最小值的次数,乘以 (nums[i]),加到答案中。
- 返回结果对 (10^9 + 7) 取模。
代码
代码支持 Python3:
Python3 Code:
1 | MOD = int(1e9) + 7 |
复杂度分析
- 时间复杂度:(O(n \log n + n \cdot k))
- 排序:(O(n \log n))。
- 预计算阶乘和逆元:(O(MX)),(MX = 10^5) 是常数。
- 遍历 (nums) 并计算组合和:(O(n \cdot k)),因为对于每个 (i),需要计算最多 (k) 个组合数。
- 空间复杂度:(O(MX)),用于存储阶乘和逆元数组。
总结
这道题的关键在于理解子序列的最大值和最小值的贡献,并利用组合数学计算每个元素出现的次数。预计算阶乘和逆元避免了重复计算组合数的开销,使得代码能在时间限制内运行。排序后分别处理最大值和最小值贡献,是一个清晰且高效的思路。
如果你有其他解法或疑问,欢迎讨论!
力扣专属折扣
力扣免费题目已经有了很多经典的了,也覆盖了所有的题型,只是很多公司的真题都是锁定的。个人觉得如果你准备找工作的时候,可以买一个会员。另外会员很多leetbook 也可以看,结合学习计划,效率还是蛮高的。
现在力扣在每日一题基础上还搞了一个 plus 会员挑战,每天刷题可以获得积分,积分可以兑换力扣周边。
如果你要买力扣会员的话,这里有我的专属力扣折扣:https://leetcode.cn/premium/?promoChannel=lucifer (年度会员多送两个月会员,季度会员多送两周会员)