小红的数组权值
[题目链接](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);
}
}
复杂度
- 时间复杂度:
,只需遍历数组一次。
- 空间复杂度:
,只用了常数个变量。

京公网安备 11010502036488号