题解: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

【核心思想】

  1. 问题分析:给定长度为 \(N\) 的序列 \(A\),求所有子区间 \([l,r]\) 的平均值之和,对 \(998244353\) 取模。\(f(l,r) = \frac{A_l + A_{l+1} + \ldots + A_r}{r-l+1}\)。直接枚举所有 \(O(N^2)\) 个子区间不可行,需要寻找数学规律优化。这是一个前缀和 + 组合计数问题,核心在于将求和式按区间长度分类,利用前缀和的前缀和快速计算。

  2. 算法选择

    • 前缀和的前缀和:设 \(s[i] = \sum_{j=1}^{i} A_j\)\(ds[i] = \sum_{j=1}^{i} s[j]\)
    • 按长度分类:对于固定长度 \(len\) 的所有区间,计算其元素和的总和,再除以 \(len\)
    • 模逆元:利用费马小定理计算除法的模意义下的逆元
  3. 关键步骤

    • 读入数据:读取 \(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\)
    • 输出结果\((ans \bmod MOD + MOD) \bmod MOD\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N \log MOD)\),预处理 \(O(N)\),枚举长度 \(O(N)\),每次快速幂求逆元 \(O(\log MOD)\)
    • 空间复杂度:\(O(N)\),前缀和数组
  5. 前缀和与组合数学的核心思想

    • 区间和的快速计算:子区间 \([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 确保结果非负
    • 适用于大规模区间统计、模意义下的有理数计算类问题

【解题思路】

img/7f26b9dde8eff6126994ba6d6daf2357.png

【算法标签】

前缀和

【代码详解】

#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
posted @ 2026-07-26 17:32  团爸讲算法  阅读(2)  评论(0)    收藏  举报