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