题意

给定一个序列,每个位置 ii 存在一种颜色 aia_i,要求支持下列两种操作:

  1. 给定子段 [l,r][l, r] 和颜色 xx,将该子段所有位置的颜色全部变为 xx
  2. 给定 kk,求所有长度为 kk 的子段内不同颜色的数量的和。
    Data Range\mathcal{Data~Range}1n,q1051 \le n, q \le 10^51ai1091 \le a_i \le 10^91kn1 \le k \le n

题解

我们发现修改操作只有一种:子段颜色覆盖。那么我们可以考虑用 set\texttt{set} 维护颜色段,根据颜色段均摊,这样的时间复杂度是可以接受的。

那么现在的问题是:求出一个颜色段对答案的贡献。

乍一看,我们似乎要维护每个位置对每一种 kk 的贡献,时间复杂度似乎不太可以接受。

但实际上我们通过分析,可以发现贡献要么呈一段相等的数的形式,要么呈一段等差数列的形式,并且我们仅需支持单点查询,所以我们可以把贡献差分,那么询问时就仅需求前缀和即可。

接下来就是具体分析的环节。

我们钦定在段中,每种颜色第一次出现的位置才会造成实质性的贡献。

那么对于第一个位置 pospos,我们考虑计算它对 kk 的贡献:

在包含 pospos 的所有长度为 kk 的子段中,右端点最右会延伸到 min(n,pos+k1)\min(n, pos + k - 1),因为极限情况就是左端点为 pospos 的情况,但是右端点又不能超过 nn

假设我们现在已经维护好了 pospos 的颜色在 pospos 之前的最后一次出现的位置 prepre(没有则记为 00),那么在包含 pospos 的所有长度为 kk 的子段中,右端点最左会延伸到 max(pos,pre+k)\max(pos, pre + k),因为极限情况就是右端点为 pospos 的情况,但是左端点又必须在 prepre 右侧(否则 pospos 的颜色的实质性贡献就不会由 pospos 提供)。

(注意 prepre 可以在维护颜色段的同时维护。)

综上,pospos 的贡献即为 min(n,pos+k1)max(pos,pre+k)+1\min(n, pos + k - 1) - \max(pos, pre + k) + 1。注意 kk 的范围为 [1,npos][1, n - pos],否则 pospos 会有负贡献,这是不存在的。

把贡献拆开,可以发现 min\minmax\max 的部分由于 kk 递增,所以贡献是呈一段等差数列和一段相等数列的形式的,差分后可以很方便地用线段树维护。

那么我们还需要对颜色段中 pospos 以后的位置做同样的分析吗?当然不需要。

我们发现,颜色段中 pospos 以后的位置 pospos' 的贡献十分简单,因为包含它的子段的左端点必须在 pospos' 这个位置上,所以它对所有 k[1,npos+1]k \in [1, n - pos' + 1] 的贡献均为 11

稍作分析,可以发现颜色段中 pospos 以后的位置对 k[1,npos]k \in [1, n - pos] 的总贡献为 min(nposk+1,len1)\min(n - pos - k + 1, len - 1)(其中 lenlen 表示颜色段长度)。由于 kk 递增,因此这个部分的贡献仍然呈一段等差数列和一段相等数列的形式,可以继续差分后用线段树维护。

于是这题就做完了,不过写起来细节还挺多。

(由于笔者 11 操作的常数过大,所以笔者做了一个数据分治,对于询问次数 qq 较小的数据采用了常数较小的、复杂度为 O(mlog2n+nq)\mathcal{O}(m\log_2 n + nq) 的做法。)

代码实现