Skip to content
模意义下最大子段和

模意义下最大子段和

2026/2/21

题面

给定长度为 nn 的数组 A={a1,a2,…,an}A = \{a_1, a_2, \dots, a_n\} 和模数 mm ,求数组 AA 模 mm 意义下的最大子段和.

形式化地,求:

max⁡1≤l≤r≤n((∑k=lrak) mod m) \max_{1 \le l \le r \le n} \left( \left( \sum_{k=l}^{r} a_k \right) \bmod m \right)

分析

显然 Kadane 算法不可行.考虑前缀和角度:

设 SS 为数组 AA 模 mm 意义下的前缀和数组,约定 S0=0S_0 = 0 .则

(∑k=lrak) mod m=(Sr−Sl−1+m) mod m \left( \sum_{k=l}^{r} a_k \right) \bmod m = (S_r - S_{l-1} + m) \bmod m

固定右端点 rr ,根据 SrS_r 和 Sl−1S_{l-1} 的大小关系分讨:

  1. Sr≥Sl−1S_r \ge S_{l-1}:
    此时 (Sr−Sl−1+m) mod m=Sr−Sl−1(S_r - S_{l-1} + m) \bmod m = S_r - S_{l-1} .
    显然 Sl−1S_{l-1} 取 S0=0S_0=0 时该值最大.此时模意义下子段和 =Sr= S_r .

  2. Sr<Sl−1S_r < S_{l-1}:
    此时 (Sr−Sl−1+m) mod m=Sr−Sl−1+m(S_r - S_{l-1} + m) \bmod m = S_r - S_{l-1} + m.
    为使该值最大,则需 Sl−1S_{l-1} 最小,故 Sl−1S_{l-1} 应取大于 SrS_r 的最小值.此时模意义下子段和 >Sr> S_r .

代码实现

暴力枚举复杂度 O(n2)O(n^2) .

可利用支持高效插入和查找的数据结构优化“查找最小的大于 SrS_r 的 Sl−1S_{l-1}”操作.如在 C++ 中可使用基于红黑树的 std::set 实现,单次操作复杂度为 O(log⁡n)O(\log n).

示例

cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#include <set>
#include <vector>
using namespace std;
using ll = long long;

ll solve(const vector<ll>& a, ll n, ll m) {
    ll ans = 0, sum = 0;
    set<ll> s;
    s.insert(0);
    for (ll i = 1; i <= n; i++) {
        sum = (sum + a[i] % m + m) % m;
        auto it = s.upper_bound(sum);
        if (it != s.end()) {
            ans = max(ans, sum - *it + m);
        } else {
            ans = max(ans, sum);
        }
        s.insert(sum);
    }
    return ans;
}

复杂度

  • 时间复杂度:O(nlog⁡n)O(n \log n).
  • 空间复杂度:O(n)O(n).