找回密码
 立即注册
首页 业界区 业界 P5574 [CmdOI2019] 任务分配问题

P5574 [CmdOI2019] 任务分配问题

嗳诿 前天 23:50
题目描述

经典的分 \(k\) 段问题,要求求出分 \(k\) 段后使每段顺序对数量之和最小,求这个最小的值。
思路

首先,我们很好得出这种分段问题的状态转移方程即 $$dp_{i,j}=\min{dp_{k,j-1}+w(k+1,i)}$$ 其中 \(dp_{i,j}\) 表示选到前 \(i\) 个数,分了 \(j\) 段的最小费用,我们可以用 \(O(n^2k)\) 的时间复杂度来实现,显然超时,得分20pts
接着考虑优化,不难发现,\(w(i+1,j)+w(i,j+1) \ge w(i,j)+w(i+1,j+1)\),所以,该式子满足四边形不等式,即可使用决策单调性来优化该状态转移方程。
P4767邮局这道题,与该题状态转移方程相同,所以一上手想到使用四边形不等式中 \(opt(i,j-1) \ge opt(i,j) \ge opt(i+1,j)\) 的性质优化该题优化该题,然而,这种算法时间复杂度为 \(O(n(n+m))\),并不能通过该题,结果超时,得分40pts,邮局一题中 \(n\) 与 \(m\) 上界相同,而该题 \(m\) 远小于 \(n\) 我们需要使用分治优化将时间复杂度降到 \(O(k \times n \log n)\) 级别才能通过。
我们解决了DP阶段的问题,接着需要解决的就是预处理 \(w\) 数组的问题了,由于我们无法接受 \(O(n^2)\) 的时间复杂度,所以我们要对其进行优化,因为数组大小的限制,预处理出 \(w\) 数组这条思路已经行不通了,我们考虑在DP过程中求一段的花费,注意到,在分治求解的过程中,当决策点每向右移动一位,我们的费用由 \(w(i,j)\) 变为 \(w(i,j+1)\) 过程中,我们增加的顺序对费用即为 \(i\) 到 \(j\) 中小于 \(a_{j+1}\) 的数的个数,而这个我们很容易实现 \(log\) 级别的时间复杂度,所以,记录 \(tl\) 和 \(tr\) 分别表示上个状态转以后已经处理完的左右端点,如果两状态位于分治中同一区间,则每次转移需要 \(O(\log n)\) 的时间复杂度,如果改变了区间,需要先跳到所求区间,设两区间分别为 \([L_i,R_i]\) 和 \([L_j,R_j]\) 所需要跳的步数即为 \(R_j-L_i\),放在分治中即可粗略计算为每层跳 \(n\) 次,每次跳跃转移点同样需要 \(O(\log n)\),综上所述,时间复杂度为 \(O(k \times n \log^2 n)\) 可以通过该题。
求顺序对时若使用线段树实现,时间超限,得分60pts,使用常数更小的树状数组实现,成功通过该题。
另外,观察状态转移方程,每次转移只和 \(j-1\) 有关,所以我们可以压缩掉一维,只记录当前与上一行状态。
代码

[code]#include#define ll long longusing namespace std;const int M=25010;int n,m;int a[M],w[M];int dp[M][2],tl=1,tr=0,sum=0;int lowbit(int x) {        return x&-x;}void add(int x,int w){        for(;xre)        {                add(w[tr],-1);                sum-=query(w[tr]);                tr--;        }    while(tl>1;        int k=mid;        for(int i=lt;i

相关推荐

您需要登录后才可以回帖 登录 | 立即注册