[DS记录] P9877 [EC Final 2021] Vacation

P9877 [EC Final 2021] Vacation

题意简述

给定长度为 \(n\) 的序列 \(a\) 和常数 \(c\),有 \(m\) 次操作,操作有以下两种类型:# P9877 [EC Final 2021] Vacation

  • \(1~x~y\),将 \(a_x\) 改为 \(y\)
  • \(2~l~r\),求出 \(\max\left(\max_{l \leq l' \leq r' \leq r\atop r'-l'+1\leq c} ~~ \left(\sum_{i=l'} ^{r'} a_i\right), 0\right)。\)

分析

题意相当于给出 \(l,r\),求 \([l,r]\) 中长度不超过 \(c\) 的最大子段和。一个经典的做法是把序列按 \(c\) 分块,那么答案就在单块中或者在两块之间。接下来需要处理的就是单块内的最大值和块与块之间的最大值。

对于单块,可以直接用线段树维护最大子段和。最大的问题在于两块之间,相当于要对于前一块的每一个后缀,在后一块中找到满足长度限制的最大前缀。形式化地来讲,就是对于两个整块之间,求 \(max_{i=1}^{c-1} \left\{suf_i + max_{j=1}^{c-i} \ pre_j \right\}\)

非常巧妙的是,这仍可以用线段树维护。考虑线段树节点 \([l,r]\) 记录左端点在 \([l,r]\),右端点在 \([l + c,r + c]\)时的信息,更加具体的来说,有:

struct data2{
    ll mx, pre, suf, sum1, sum2; // 注意这里的pre是下一段的前缀和,suf是这一段的后缀和
    data2(int x = 0, int y = 0) : mx(max(x, 0)), pre(max(y, 0)), suf(max(x, 0)), sum1(x), sum2(y) {}
    data2 operator + (const data2 &o) const{
        data2 res;
        res.mx = max(mx + o.sum1, max(o.mx + sum2, o.suf + pre));
        res.pre = max(pre, sum2 + o.pre);
        res.suf = max(o.suf, o.sum1 + suf);
        res.sum1 = sum1 + o.sum1;
        res.sum2 = sum2 + o.sum2;
        return res;
    }
};

主要要说一下 \(mx\),它记录的是左端点在 \([l,r]\) 中,右端点在 \([l+c,r+c]\) 中的最大值,在最后求跨区间子段和的时候,用 \(mx\) 再加上 \(\sum_{r + 1}^{l + c - 1}\) 就是这两段之间的最大子段和(因为一定要跨区间所以要加上中间这一段)。

接下来我们就可以愉快地考虑如何求答案了。作以下分类讨论:

  • \(r - l + 1 \le c\)。此时直接求区间最大子段和(可能需要再开一棵线段树)。

  • \(l,r\) 不在相邻的块中。那么对于中间的整块,需要查询整块内部和整块两两之间的答案。然后对于 \([l,R_{\ bl}], [L_{\ br - 1}, r - c]\) 分别求跨区间子段和。

  • \(l,r\) 在相邻的块中,那么只能对 \([l,r - c]\) 的部分询问跨区间的子段和。

  • 对于后两种情况,最大子段和还有一种可能性来自于 \([l,\ l+c-1]\) 或者 \([r-c+1,\ r]\) 区间中,要再对这两个区间询问最大子段和。

这道题到这里就差不多做完了,实现的时候稍微有一些细节。

这道题有一些比较有启发性的地方: 第一点是对于“长度不超过 \(c\)”这一类问题,可以考虑按照该长度限制分块,从而只用考虑单块内和两块之间的情况;第二点是用线段树维护长度限制下的两块间的前一块的最大后缀加上后一段的最大前缀,这个维护的做法非常巧妙。

Bonus:

其实我拿到题的时候完全没有往这方面去想,而是想的直接对序列分块。那么单块内的情况可以直接用单调队列求出。考虑跨多块的情况,这个时候似乎必须要枚举区间左右端点所在的块,那么对于中间的整块直接加起来。则对于左右端必须 \(O(1)\) 做完。考虑如果此时剩下长度 $ \ge 2c$,那么一定可以选到左右端最大值。那么只有在 \(O(1)\) 个情况下(即对于每一个块,只有 \(O(1)\) 个块需要考虑长度不足 \(2c\)),对于这些情况,在修改到其中某个段的时候对其进行修改即可。

但是,显然地,这样做的复杂度是 \(O(q(B + \frac{n^2}{B}))\),取 \(B=5000\)左右比较平衡,但因为常数不小而且复杂度本来就不如正解,于是就没有写。

posted @ 2026-01-15 20:07  lyc2049  阅读(0)  评论(0)    收藏  举报