Codeforces 834D
Posted littlewyy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces 834D相关的知识,希望对你有一定的参考价值。
原题链接
题意
给你一个长度为n的序列,要求将其分成k段,每一段的贡献是这一段中不同的数的个数,求最大总贡献。
\(1\leq n \leq 35000,1\leq k \leq min(n,50)\)
题解
状态:\(f(i)(j)\)表示前\(i\)个数分成\(j\)段的最大贡献
转移:\(f(i)(j) = max_k=0^i-1f(k)(j-1)+w(k+1,i)\)
时间复杂度:\(O(n^2k)\)
观察转移方程,不难发现\(f(i)(j)\)的决策集合与\(f(i-1)(j)\)的决策集合有重合性;且附加值呈现区间性变化:所有\(w(k',i)\)比\(w(k',i-1)\)多1,其中\(k'\)在\(i\)的前一个同类元素\(pre(i)\)之后;其余\(w\)不变。
区间增加,最值查询,不妨考虑数据结构维护。
将\(f(i = 1 \rightarrow n )(j-1)\)全部载入线段树中。处理\(f(i)(j)\)时,在线段树中将\([pre(i) , i - 1]\)区间加1,再对\([0,i-1]\)取\(max\)即可。
时间复杂度:\(O(nlognk)\)。代码见此
另解
根据决策集合的重合性和附加值仅末尾一段增加的性质,不难得出一个结论:
\(f(i)(j)\)相对于\(f(i-1)(j)\)的最优决策点单调不左移。
当dp具有决策单调性时,可以使用分治法求解。
具体地,计算完\(f(1\rightarrow n)(j-1)\)后,整体地转移到\(f(1 \rightarrow n)(j)\)。对于\(f(l \rightarrow r)(j)\)的求解,令$mid = (l + r) /2 \(,暴力求出\)f(mid)(j)\(及其最优决策点\)dmid\(,则可确定\)f(l\rightarrow mid)(j)\(的决策点在\)dmid\(及其左边,\)f(mid +1 \rightarrow r)(j)\(的决策点在\)dmid$及其右边,递归下去求解即可。
分治至多\(logn\)层,每层时间复杂度\(O(n)\)。共进行\(k\)次分治求解。
时间复杂度:\(O(nlognk)\)。代码见此
实现上的细节问题:注意分治的意义在于决策区间被限定在\([ll,lr]\),递归函数内注意循环的范围。另外,快速求解\([l,r]\)中不同数的种类数,可以使用差分+可持久化线段树。若每次计算\(w\)都调用线段树,复杂度会多一个log;实际上只需调用1次,计算出\([lr+2,mid]\)中不同数的个数,其余在从后往前枚举决策点时累加即可。
以上是关于Codeforces 834D的主要内容,如果未能解决你的问题,请参考以下文章
Codeforces 834D The Bakery - 动态规划 - 线段树