小红的数组权值

[题目链接](https://www.nowcoder.com/practice/7bcd78aec6e74ebf9061245e5936c18d)

思路

题意

定义长度为 的数组 的权值为 。给定长度为 的数组 ,求所有连续子数组的权值之和,对 取模。

贡献法

暴力枚举所有子数组再逐个计算权值是 的,无法通过。我们换一个角度:考虑每个元素 索引)对最终答案的总贡献。

对于子数组 ),元素 在该子数组中的下标为 (从 开始),因此它的贡献系数为

所以 对答案的总贡献为:

$$

注意到 不出现在 中,内层对 求和直接变为乘以

$$

因此答案为:

$$

样例验证

数组

贡献
0 1 3 1 3
1 2 2 3 12
2 3 1 6 18

总和 ,与样例一致。

取模处理

由于除以 在模运算下不能直接做整除,需要乘以 的模逆元。因为 是奇数, 的逆元为

代码

#include <bits/stdc++.h>
using namespace std;

int main() {
    int n;
    scanf("%d", &n);
    const long long MOD = 1000000007;
    const long long inv2 = (MOD + 1) / 2;
    long long ans = 0;
    for (int j = 0; j < n; j++) {
        long long a;
        scanf("%lld", &a);
        a %= MOD;
        long long coeff = (1LL * (n - j) % MOD) * ((1LL * (j + 1) % MOD * ((j + 2) % MOD)) % MOD) % MOD;
        coeff = coeff % MOD * inv2 % MOD;
        ans = (ans + a % MOD * coeff % MOD) % MOD;
    }
    printf("%lld\n", (ans % MOD + MOD) % MOD);
    return 0;
}
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        long MOD = 1000000007L;
        long inv2 = (MOD + 1) / 2;
        long ans = 0;
        for (int j = 0; j < n; j++) {
            long a = sc.nextLong();
            a %= MOD;
            if (a < 0) a += MOD;
            long nj = (n - j) % MOD;
            long j1 = (j + 1L) % MOD;
            long j2 = (j + 2L) % MOD;
            long coeff = nj % MOD * (j1 % MOD * (j2 % MOD) % MOD) % MOD;
            coeff = coeff % MOD * inv2 % MOD;
            ans = (ans + a * coeff % MOD) % MOD;
        }
        System.out.println((ans % MOD + MOD) % MOD);
    }
}

复杂度

  • 时间复杂度:,只需遍历数组一次。
  • 空间复杂度:,只用了常数个变量。