传送门:https://codeforces.com/contest/1527/problem/E
$当时E题没有时间写,但是20分钟足够了。$
题意
$将一个长为n的序列,分成k段。$ $在每段中,对于每个数字来说,贡献为最后一次出现的位置减第一次出现的位置,即last(x)-first(x)$
$求分成k段后,最小的总贡献。$
思路
$n分成k段,再看看数据范围,n\leq 35000,k\leq 100,dp[n][k]$
$dp[n][k]表示前n个数字分成k段后的最小总贡献。$ $那么转移方程就非常好写:$
$$dp[i][j]=dp[i-1][k]+val[k+1][j]$$ $val[i][j]表示[i,j]内所有数字的贡献。$ $很容易推出k \in [i-1,j-1]$
$这样我们的复杂度为O(n^2k),是不行的,不过O(nk)是可以的,所以我们想办法消掉一个n。$
$因为我们的dp[i][j]都是由dp[i-1][j-1]推出来的。$ $而我们的目的就是找到一个k使得dp[i-1][k]+val[k+1][j]最小,而k \in [i-1,j-1].$ $进而我们可以用线段树围绕k维护区间最小值,这样我们可以在log内找到这样的k进行转移。$
$假设我们当前要将第j个数字分类,则[1,前一个位置-1]的贡献都要改变,变多少呢?多j-(前一个位置)。$
$也就是修改:$
1 | if(from[j] != -1) modify(1, 1, from[j] - 1, j - from[j]); |
$查询是在[i-1,j-1]找,也就是:$
1 | dp[j][i] = min(dp[j - 1][i - 1], query(1, i - 1, j - 1)); |
$最终时间复杂度为O(nk\log n)$
Code
1 | # |
本文作者:jujimeizuo
本文地址: https://blog.jujimeizuo.cn/2021/05/21/codeforces-round-721-div-2-e-partition-game/
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0 协议。转载请注明出处!