题解:AtCoder AT_abc468_e Sum of Average
【题目来源】
AtCoder:Sum of Average
【题目描述】
You are given a positive integer \(N\) and a length-\(N\) integer sequence \(A=(A_1,A_2,\ldots,A_N)\).
Define \(f(l,r)\) as the (arithmetic) mean of \(A_l,A_{l+1},\ldots,A_r\).
Find \(\displaystyle \sum_{1\le l\le r\le N} f(l,r)\), modulo \(998244353\).
Definition of a rational number modulo \(998244353\)
Under the constraints of this problem, it can be proved that the rational number to be found is always an irreducible fraction \(\frac{P}{Q}\) such that \(Q {{}\not\equiv{}} 0 \pmod{998244353}\). Thus, there is a unique integer \(R\) such that \(R \times Q \equiv P \pmod{998244353}, 0 \leq R < 998244353\). Output this \(R\).
给定一个正整数 \(N\) 和一个长度为 \(N\) 的整数序列 \(A=(A_1,A_2,\ldots,A_N)\)。
定义 \(f(l,r)\) 为 \(A_l, A_{l+1}, \ldots, A_r\) 的(算术)平均值。
求 \(\displaystyle \sum_{1\le l\le r\le N} f(l,r)\),对 \(998244353\) 取模。
有理数对 \(998244353\) 取模的定义
在本题的约束条件下,可以证明所求的有理数总可以表示为最简分数 \(\frac{P}{Q}\),且满足 \(Q \not\equiv 0 \pmod{998244353}\)。因此,存在唯一的整数 \(R\) 满足 \(R \times Q \equiv P \pmod{998244353}\),\(0 \leq R < 998244353\)。输出这个 \(R\)。
【输入】
The input is given from Standard Input in the following format:
\(N\)
\(A_1\) \(A_2\) \(\ldots\) \(A_N\)
【输出】
Output the answer.
【输入样例】
2
2 3
【输出样例】
499122184
【核心思想】
-
问题分析:给定长度为 \(N\) 的序列 \(A\),求所有子区间 \([l,r]\) 的平均值之和,对 \(998244353\) 取模。\(f(l,r) = \frac{A_l + A_{l+1} + \ldots + A_r}{r-l+1}\)。直接枚举所有 \(O(N^2)\) 个子区间不可行,需要寻找数学规律优化。这是一个前缀和 + 组合计数问题,核心在于将求和式按区间长度分类,利用前缀和的前缀和快速计算。
-
算法选择:
- 前缀和的前缀和:设 \(s[i] = \sum_{j=1}^{i} A_j\),\(ds[i] = \sum_{j=1}^{i} s[j]\)
- 按长度分类:对于固定长度 \(len\) 的所有区间,计算其元素和的总和,再除以 \(len\)
- 模逆元:利用费马小定理计算除法的模意义下的逆元
-
关键步骤:
- 读入数据:读取 \(N\) 和数组 \(A[1..N]\)
- 预处理前缀和:
- \(s[i] = (s[i-1] + A_i) \bmod MOD\)
- \(ds[i] = (ds[i-1] + s[i]) \bmod MOD\)
- 按区间长度枚举(\(len\) 从 \(1\) 到 \(N\)):
- 计算所有长度为 \(len\) 的区间的元素和之和:
- \(\Delta = ds[N] - ds[len-1] - ds[N-len]\)
- 解释:\(ds[N] - ds[len-1]\) 是 \(s[len] + s[len+1] + \ldots + s[N]\),减去 \(ds[N-len]\) 消除多余部分
- 长度为 \(len\) 的区间的平均值之和 = \(\Delta \times inv(len) \bmod MOD\)
- \(ans \leftarrow (ans + \Delta \times inv(len)) \bmod MOD\)
- 计算所有长度为 \(len\) 的区间的元素和之和:
- 输出结果:\((ans \bmod MOD + MOD) \bmod MOD\)
-
时间/空间复杂度:
- 时间复杂度:\(O(N \log MOD)\),预处理 \(O(N)\),枚举长度 \(O(N)\),每次快速幂求逆元 \(O(\log MOD)\)
- 空间复杂度:\(O(N)\),前缀和数组
-
前缀和与组合数学的核心思想:
- 区间和的快速计算:子区间 \([l,r]\) 的和为 \(s[r] - s[l-1]\)。对于固定长度 \(len\),所有区间 \([l, l+len-1]\) 的和之和为 \(\sum_{l=1}^{N-len+1} (s[l+len-1] - s[l-1])\)
- 前缀和的前缀和优化:\(ds[i] = \sum_{j=1}^{i} s[j]\),则 \(\sum_{l=1}^{N-len+1} s[l+len-1] = ds[N] - ds[len-1]\),\(\sum_{l=1}^{N-len+1} s[l-1] = ds[N-len]\),因此 \(\Delta = ds[N] - ds[len-1] - ds[N-len]\)
- 模逆元替代除法:在模 \(998244353\)(质数)下,除以 \(len\) 等价于乘以 \(len^{MOD-2} \bmod MOD\),利用快速幂计算
- 负数处理:取模运算中减法可能导致负数,通过
(x % MOD + MOD) % MOD确保结果非负 - 适用于大规模区间统计、模意义下的有理数计算类问题
【解题思路】

【算法标签】
前缀和
【代码详解】
#include <bits/stdc++.h>
using namespace std;
#define int long long // 将int定义为long long,避免中间计算溢出
const int N = 500005, MOD = 998244353; // N为数组最大容量,MOD为模数998244353
int n, ans; // n为序列长度,ans存储最终答案
int a[N], s[N], ds[N]; // a存储原数组;s为前缀和数组(s[i]=A_1+...+A_i);ds为前缀和的前缀和(ds[i]=s[1]+...+s[i])
// 快速幂算法:计算a的b次幂对MOD取模的结果
// 用于求逆元:根据费马小定理,i的逆元为i^(MOD-2) mod MOD(MOD为质数)
int power(int a, int b)
{
int res = 1; // res存储幂的结果
a %= MOD; // 先将底数对MOD取模
while (b > 0) // 当指数b大于0时继续循环
{
if (b & 1) // 如果b的最低位为1(即b为奇数)
{
res = res * a % MOD; // 将当前底数乘入结果
}
a = a * a % MOD; // 底数平方(为下一位做准备)
b >>= 1; // 指数右移一位(相当于除以2)
}
return res; // 返回a^b mod MOD
}
signed main() // 使用signed main配合#define int long long
{
cin >> n; // 读入序列长度N
for (int i = 1; i <= n; i++) // 读入N个整数
{
cin >> a[i];
// 同时计算前缀和s和前缀和的前缀和ds
s[i] = (s[i - 1] + a[i]) % MOD; // s[i] = (A_1 + A_2 + ... + A_i) mod MOD
ds[i] = (ds[i - 1] + s[i]) % MOD; // ds[i] = (s[1] + s[2] + ... + s[i]) mod MOD
}
// 枚举区间长度i(从1到N),计算所有长度为i的区间的平均值之和
for (int i = 1; i <= n; i++) // i表示当前枚举的区间长度
{
// delta = ds[N] - ds[i-1] - ds[N-i]
// 推导:所有长度为i的区间的和 = 以每个位置结尾的长度为i的区间和之和
// 利用前缀和的前缀和ds快速计算
int delta = ds[n] - ds[i - 1] - ds[n - i];
// 处理负数:先取模再加MOD再取模,确保结果在[0, MOD)范围内
delta = (delta % MOD + MOD) % MOD;
int invi = power(i, MOD - 2); // 计算i在模MOD下的逆元(费马小定理)
// 所有长度为i的区间的平均值之和 = delta / i = delta * invi mod MOD
ans = (ans + delta * invi) % MOD; // 累加到总答案中
}
// 确保答案非负:先取模再加MOD再取模
cout << (ans % MOD + MOD) % MOD << endl; // 输出最终答案
return 0;
}
【运行结果】
2
2 3
499122184
浙公网安备 33010602011771号