程序员老鬼

字节员工吐槽:年薪100万,依然被媳妇挂在网上,被嘲扶贫。。

刚看到个贴子,字节的员工吐槽自己年入百万,结果还被枕边人拿去网上嘲笑“扶贫”。

Image

网友有说“人中龙凤也不行”“赚钱没用”,也有人觉得“有钱就够了别矫情”。但我觉得,这事不是钱多钱少的问题,而是价值观错位。

一个拼命赚钱的打工人,却被最亲近的人看不起,这种精神落差才最伤人。

说到底,互联网人不是“扶贫”,是努力换生活。挣再多也想被理解,而不是被贬低。别用别人的职业来定义尊严,每个人都在自己的位置上辛苦生活。

尊重劳动,才是最该普及的底层逻辑。【备注:文末可领最新资料】

算法题:巫师的总力量和

有一排巫师,每个巫师有个“力量值”a[i]。任意连续一段的“队伍总力量”= 这段里最小值 × 这段元素和。题目要所有子数组的“队伍总力量”之和,结果很大取模 1e9+7。

直接枚举子数组会超时。换个角度:固定某个位置 i 当成它所在子数组的最小值,把它对答案的贡献一次算清。 关键是找出所有把 a[i] 当最小值的子数组范围,这通常用单调栈做“左右边界”:

  • 左边界 L:离 i 最近、严格小于 a[i] 的位置(没有就设为 -1)。
  • 右边界 R:离 i 最近、小于等于 a[i] 的位置(没有就设为 n)。 用“左<、右≤”这一套,比“都<”更容易去重。

这样,i 左边可选的起点个数是 left = i - L,右边可选的终点个数是 right = R - i。但我们还需要这些子数组的区间和。直接累加仍然会慢,于是再上一个经典招:前缀和的前缀和(双前缀)。

定义:

  • pre[k] = a[0] + … + a[k-1](长度为 n+1)
  • pp[k] = pre[0] + … + pre[k-1](长度为 n+1)

有了它们,可以 O(1) 求“若干区间和的总和”。最终结论(推导略):

对每个 i 的贡献:

sumRight = pp[R+1] - pp[i+1]     // 相当于聚合了所有以右端点落在 [i, R-1] 的区间和
sumLeft  = pp[i+1] - pp[L+1]     // 相当于聚合了所有以左端点落在 [L+1, i] 的区间和
贡献 = a[i] * ( sumRight * left - sumLeft * right )

全程取模,注意负数要加模。

复杂度与坑

  • 单调栈两次扫:O(n)。
  • 预处理双前缀:O(n)。
  • 贡献求和:O(n)。 总体 O(n),内存 O(n)。 坑点: 1)右边界用“≤”,左边界用“<”,否则重复或漏计。 2)中间乘法、减法都要上 long 与取模防溢出与负数。

Java 实现

import java.util.*;

publicclassSolution{
staticfinallong MOD = 1_000_000_007L;

publicinttotalStrength(int[] a){
int n = a.length;
// 1) 前缀和 pre,二级前缀 pp
long[] pre = newlong[n + 1];
for (int i = 0; i < n; i++) pre[i + 1] = (pre[i] + a[i]) % MOD;

long[] pp = newlong[n + 1];
for (int i = 0; i < n; i++) pp[i + 1] = (pp[i] + pre[i + 1]) % MOD;

// 2) 左边界:prevLess(严格小于)
int[] L = newint[n];
        Deque<Integer> st = new ArrayDeque<>();
for (int i = 0; i < n; i++) {
while (!st.isEmpty() && a[st.peek()] >= a[i]) st.pop(); // 为了右边用≤,这边配套成 < :这里要“>=”弹出
            L[i] = st.isEmpty() ? -1 : st.peek();
            st.push(i);
        }

// 3) 右边界:nextLessEqual(小于等于)
int[] R = newint[n];
        st.clear();
for (int i = n - 1; i >= 0; i--) {
while (!st.isEmpty() && a[st.peek()] > a[i]) st.pop(); // 右边要“≤”保留相等,因此这里弹出“>”
            R[i] = st.isEmpty() ? n : st.peek();
            st.push(i);
        }

// 4) 累加贡献
long ans = 0;
for (int i = 0; i < n; i++) {
int left = i - L[i];
int right = R[i] - i;

long sumRight = (pp[R[i] + 1] - pp[i + 1]) % MOD; // pp索引+1别写错
long sumLeft  = (pp[i + 1] - pp[L[i] + 1]) % MOD;

if (sumRight < 0) sumRight += MOD;
if (sumLeft  < 0) sumLeft  += MOD;

long contrib = ( (sumRight * left) % MOD - (sumLeft * right) % MOD ) % MOD;
if (contrib < 0) contrib += MOD;

            ans = (ans + contrib * a[i]) % MOD;
        }
return (int) ans;
    }
}

这题的“味儿”就是:栈定最小覆盖范围 + 双前缀批量求区间和。别被公式吓到,代码其实很直。

-END-

我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html

最后给大家分享一份不错的副业资料,点击下方公众号,回复关键字: 副业 领取,也可以链接我领取,微信:hls404