BZOJ 4540 [Hnoi2016]序列 | 莫队 详细题解

时间:2023-03-09 02:27:42
BZOJ 4540 [Hnoi2016]序列 | 莫队 详细题解

传送门

BZOJ 4540

题解

……怎么说呢……本来想写线段树+矩阵乘法的……
……但是嘛……yali的机房太热了……困……写不出来……
于是弃疗,写起了莫队。(但是我连莫队都想不出来!)

首先用单调栈可以轻松求出每个元素左右两边第一个比它小的元素位置,分别设为tl[i]、tr[i]。

莫队关键就是在一端插入/删除元素时如何维护当前区间的答案嘛,而这就需要快速计算当前区间左/右端点对当前区间的贡献。

下面以在左边插入一个元素为例,设插入后区间是[l, r]。

插入这个元素的时候,新增的区间显然是左端点为l、右端点为l~r的所有区间。它们的贡献按照最小值分类,可以分为:最小值为a[l]的、最小值为a[tr[l]]的、最小值为a[tr[tr[l]]]的……最小值为[l, r]区间最小值(它的位置设为p)的。

那么每个最小值 * 最小值为它的区间个数,再加起来,就是左端点的贡献。

为了快速求出,可以对每个元素维护一个“前缀和”,表示以这个元素为左/右端点的所有区间的最小值之和,分别设为sr[i]和sl[i]。以sr[i]为例:

sr[i] = sr[tr[i]] + a[i] * (tr[i] - i)

然后,区间[l, r]左端点的贡献就是sr[i] - sr[p] + a[p] * (r - p +1)。
右边a[p] * (r - p + 1)这一块是因为以a[p]为最小值的区间不一定都包含在[l, r]内,有的右端点在r的右面。

这样,结合莫队,就可以AC了!

……(低级失误:用st表维护的是下标,却把值作为初值传了进去……还有把查询时的j打成j-1……)