SP685 SEQPAR - Partition the sequence Solution

更好的阅读体验戳此进入

(建议您从上方链接进入我的个人网站查看此 Blog,在 Luogu 中图片会被墙掉,部分 Markdown 也会失效)

题面

给定 n 个数的数列 an,给定 k,将数列分成 k 段,满足每一段的元素和不大于 m,求最小的 mT 组数据。

1kn1.5×104,3×104ai3×104,1T10

Solution

首先二分答案很显然,二分 m,并验证 m 是否对于 k 成立。

然后对于验证 m 是否成立,我们显然可以想到,对于这样的一个数列和 m,有解的情况下,一定存在一个最小的和最大的块数的划分方案,并且解是存在连续性的,对于最小 fmn 和最大 fmx 一定有 k[fmn,fmx] 都成立,这个的严谨证明我不会很显然,就不证明了。

The proof is left to the reader.

然后对于求 fmn(i)fmx(i),令其为考虑前 i 个数的合法方案的最小和最大的 k,求个前缀和 sum(i),显然有如下的 DP 方程:

fmn(i)=min(fmn(j)+1)j<isum(j)sum(i)mfmx(i)=max(fmx(j)+1)j<isum(j)sum(i)m

对于二分的范围,不难想到我们令数列中负数的和为 ξ,正数的和为 ϵ,那么范围即为 [ξ,ϵ],我们令这个区间长度为 v

发现这个东西的复杂度是 O(n2) 的,最终复杂度 O(Tn2logv),显然会寄,于是考虑优化 DP。

显然我们发现这个转移时可以优化的,可以考虑写个平衡树然后区间取最大最小值等,但是我不会不好写,于是我们考虑线段树。

普通线段树显然开不下,所以我们要写一个动态开点的权值线段树,其中的叶子节点下标表示 sum(i) 的值,存的是 fmn(i)fmx(i) 的值,然后支持单点修改和区间求 minmax。考虑在每次验证的时候新开一个线段树,然后每次查询 [sum(i)m,inf] 区间的 minmax 然后 +1 作为新的 fmn(i)fmx(i),然后再把这两个值插到线段树中对应的 sum(i) 中,具体实现可以看一下代码,这道题还是挺巧妙的。

对于权值线段树的范围,显然与二分限制相同。

那么最终复杂度为 O(Tnlognlogv)

Code

UPD

update-2022_10_13 初稿