1 条题解

  • 0
    @ 2026-10-26 2:29:30

    思路与算法

    设S[i]为前i个数的和,连续段(j+1)..i的和为S[i]-S[j]。长度在1..m内,等价于j处于[max(0,i-m),i-1],因此每个右端点只需减去这一窗口中的最小前缀和。

    维护前缀下标的单调队列,队内对应S值严格递增。处理i时,先删掉下标小于i-m的过期候选,再用队首更新最大和;随后从队尾删掉S值大于等于S[i]的旧候选,最后插入i。初始加入前缀下标0。

    必须先查询再插入i,保证选取的是非空区间;答案初始化为足够小的负数,才能处理全负序列。

    正确性说明

    任意以i结尾的合法非空区间,都与上述窗口中一个前缀下标j一一对应。选取最小S[j]便使区间和最大。

    较早的候选a若满足S[a]>=S[b]且a<b,则对未来右端点,b给出的区间和不小于a,且b不会比a更早过期,所以a可以永久删除。过期删除保证候选仍在长度限制内,队列单调性保证队首是合法窗口的最小值。枚举全部右端点,因此得到全局最大非空限长连续和。

    复杂度

    前缀数组与队列初始化O(n)。每个下标入队一次、至多出队一次,总时间O(n),空间O(n)。m大于n时算法仍然有效,无需额外扩大存储。

    易错点

    过期条件是下标<i-m,左边界i-m仍应保留。插入当前前缀之前就要查询,否则空段会让全负序列错误输出0。m可以大于n;m=1时答案是最大单个元素。保留相等前缀中较新的下标,可使候选更晚过期。

    参考代码(C++11)

    #include <bits/stdc++.h>
    using namespace std;
    using ll = long long;
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int n, m;
        cin >> n >> m;
        vector<ll> prefix(n + 1);
        vector<int> q(n + 1);
        int head = 0, tail = 1;
        q[0] = 0;
        ll answer = LLONG_MIN;
        for (int i = 1; i <= n; ++i) {
            ll x;
            cin >> x;
            prefix[i] = prefix[i - 1] + x;
            while (head < tail && q[head] < i - m)
                ++head;
            answer = max(answer, prefix[i] - prefix[q[head]]);
            while (head < tail && prefix[q[tail - 1]] >= prefix[i])
                --tail;
            q[tail++] = i;
        }
        cout << answer << '\n';
    }
    
    • 1

    信息

    ID
    1647
    时间
    1000ms
    内存
    512MiB
    难度
    普及
    标签
    递交数
    1
    已通过
    1
    上传者