题目链接
题目大意
现在有一个数列,长度为$n$,其中每个数均$\le n$,要求把数列分成若干段,使得这个数列每段中只出现$1$次的数均不超过$k$个。
$k \le n \le 10^5$
做法
首先$n^2$的$dp$非常显然。
我们考虑构造一个序列$v$,$\sum\limits_{i=l}^{r}v_i$表示区间$[l,r]$中仅出现一次的数的个数。
考虑如何构造$v$
我们每次新加进来一个数,$v_i=1$,$v_{pre[i]}=-1$,$v_{pre[pre[i]]}=0$,$pre[i]$是所有位置上的数与我相同的位置中最靠近我的一个,当然,是在我前面。
我们在对$v$做一次前缀和,得到数组$S$,那么我们上面的式子可以改写为
把问题转化一下。
维护一个数列,支持区间加,以及区间查询$S$小于一个数的$f$的和。
我们对原序列分块,每个块内维护权值的后缀和,我们对于每个区间用一个$lazy$标记维护整个块都加的值。如果有一个块有一部分被修改了,我们就暴力重构整一个块,所以每次修改的复杂度是$O(块大小)+O(块数)$,可以做到$\sqrt n$。
询问的时候,如果是整块,那么就先处理一下$lazy$标记,再在后缀和上查询一下就好了。
非整块部分暴力即可。
这题还有一些比较好的性质,我们每次插入一个数都只会改变最多两个区间的$S$值。
此外,在块内维护后缀和的时候还有一些小$tips$,发现$abs(S_i-S_{i-1})\le 1$的,所以我们把一个块内所有的$S$值,都减去最小的$S$值,那么$S$的区间是不会超过块大小的。最后在在整个区间的$lazy$标记上把减去的那个数加回来就好了。
代码
1 |
|