DP 优化

1|0DP优化

一些典型或者不典型的 DP 优化实例。

1|1CF354D Transferring Pyramid

题意:有一个 n 层的金字塔,现在能进行两种操作,一是给某个点染色,代价是 3,或者画一个底边是金字塔底边的子三角形,给每个点,代价是点数 +2,要求用最小代价覆盖所有要染色的 m 个点。

思路:定义 hi 表示点 i 到底边的距离,那么要用第二种方法覆盖这个点的代价是 O(hi2) 的,因此只有 him 的点才有可能用第二种方法覆盖。考虑沿着对角线从左下到右上进行 dp,维护操作二的轮廓线高度,即设 f(i,j) 表示转移到第 i 条对角线,轮廓线最高高度为 j 的最小代价。转移时有延续之前的轮廓线或者让之前的轮廓线断开两种,再加上 hi>m 的即可。

复杂度 O(nm)

trick:根据题目中的条件分析实际有用的范围,优化状态量。

1|2P3506 [POI2010]MOT-Monotonicity 2

题意:给定长为 n 的序列 a 和长为 k s,要求选出一个长度最长的子序列 p1,p2pk,使得 pipi+1 的关系是 s[(i1)modk+1]

思路:一开始的想法有点接近正解,不过还是差不少。首先,可以大胆猜想,以 i 为结尾的最长合法子序列一定是由某个以 j(j<i) 结尾的最长合法子序列后面加上 i 得到的。我们来证明一下。

f(i) 表示 i 结尾的最长合法子序列长度。首先,假设以 n 结尾的最长合法子序列不是由一个位置的最长合法子序列得来的,设这个子序列的上一个是 m,那么首先有 f(n)1<f(m),f(n)f(m)。假设有 am=an,那么显然可以直接在 f(m)1 后面接 n,这样不劣;假设 am<an,再分 af(m)1<anaf(m)1an 两种。对于前者,直接在后面把 n 接上即可;对于后者,一定存在一个 wf(n) 满足关系符号为 >,那么把 n 接在第 w+1 个位置,一定更优。于是就证完了。

既然证明了满足最优子结构,就说明每次可以选择合法的最大答案,可以直接用树状数组维护。

trick:最优子结构的体现。

1|3P6564 [POI2007] 堆积木KLO

题意:给定序列 a,可以任意删除一些数,删除后后面的位置会向前补齐,求最后序列中最多有多少个数满足 ai=i

思路:我们记 fi 表示钦定最后 i 在自己位置上时 i 前最多有多少个满足条件的数,然后可得:fi=max(fj|j<i,aj<ai,aiajij)+1。朴素转移是 O(n2) 的,我们尝试把合法的 j 变成一个前缀的形式。由 aiajij 可以推出 jajiai,因此先排除不合法(即 ai>i )的数,再按 iai 排序后DP,然后用树状数组维护最大值即可。复杂度 O(nlog(n))

trick:分析出转移顺序,再按照指定顺序转移会更方便。

1|4P4099 [HEOI2013]SAO

题意:给定一颗树,遍历方向为根到叶子或叶子到根,求遍历方案数。

思路:看到求拓扑序方案数首先想到设 f[i][j] 表示 ii 的子树里拓扑序排名为 j 的方案数,然后考虑合并时如何转移。f[x][k]=i+j=kf[x][i]×f[y][j]×Ck1i1×Csiz[u]+siz[v]ksiz[u]i,这个转移是 O(n3) 的,但是我们发现这个组合数与 f[y][j] 无关,也就是说可以用前缀和优化转移,这样复杂度就是 O(n2) 的了。注意转移时根据遍历方向 j 的范围会不一样,需简单分个类。

trick:前缀和优化转移。

1|5P7532 [USACO21OPEN] Balanced Subsets P

题意:有 n×n 的网格,每个位置是 0/1,求有多少个 1 连通块满足同一行或列的两点间没有空。

思路:一开始想错了,想到今年 IOI D1T3 去了,后来才发现就是其实是凸多边形。

那么就比较简单了,设 f[i][l][r][0/1][0/1] 表示考虑到第 i 行,当前左右端点的状态,上一行是否包含这一行选的区间,然后用前缀和优化即可。

trick:对于转移是从矩形内转移过来,可以用二维前缀和优化。

1|6[AGC007E] Shik and Travel

题意:一颗 n 个节点的二叉树,每个节点要么有两个儿子要么没有儿子。边有边权。

你从 1 号节点出发,走到一个叶子节点。然后每一天,你可以从当前点走到另一个叶子。最后回到 1 号节点,要求到过所有叶子并且每条边经过恰好两次

每天的路费是你走过的路径上的边权和,你的公司会为你报销大部分路费,除了你旅行中所用路费最高的,行走路线是从叶子到叶子的那一天的路费。

求路费的最小值。

思路:首先,显然可以二分答案。接着就是判定是否合法。设 dp[x][a][b] 表示在 x 的子树中,第一步走的距离是 a,最后一步是 b 是否可行,转移时枚举 i,j 表示左右儿子之间的连边在子树中的部分,那么有 dp[x][a][b]=f[ls][aw[ls]][i]&f[rs][j][bw[rs]](i+j+w[ls]+w[rs]mid)

这样 DP 很暴力考虑怎么优化。我们发现,如果有两个合法状态 f[x][a][b],f[x][c][d](ac,bd),那么 f[x][c][d] 显然是没用的,于是可以把这样的无用状态去掉,即按 a 排序后,b 必须是递减的。在合并的时候,我们可以用双指针求出 ls,rs 中的每一对对应的状态,因此每次会增加 2min(L,R),其中 L,R 表示左右子树中的状态数,因此最后的总状态数是 O(nlogn) 的。

trick:如果状态之间存在偏序关系,就可以优化状态量。

1|7P1912 [NOI2009] 诗人小G

题意:把序列划分成若干段,每一段的代价是 (airlx)p,求最小代价。

思路:O(n2) 的 DP 是好想的,考虑优化。

这个 p 次方很容易让人想到决策单调性,而且确实可以证明,可以根据导数是递增的来证。

于是可以用二分栈的经典做法来实现,复杂度 O(nlogn)

trick:决策单调性。

1|8P3571 [POI2014]SUP-Supercomputer

题意:有一棵 n 个点的树,q 次询问,每次给出一个 k,要求用最少的次数遍历完整棵树,每次可以访问不超过 k 个未被访问过且父节点已经被访问过的点。

思路:首先有结论:记 fkk 的答案,那么有 fk=max(i+si+1k),其中 si 表示深度不小于 i 的点的个数。这个式子的意思是先花 i 步走完前 i 层,然后在每次遍历 i 个点的次数。

我们来证明一下为什么是取 max。首先,如果不能花 j 步走完前 j 层,那么有 si+1sj+1>(ji)×k,那么 fi>fj,需要取 max,其次,假设前 i 次每次走一层后不能每次走 k 个点,前 j 次每次走一层后可以每次走 k 个点,那么就有 si+1sj+1<k(ji),于是 fi>fj。于是就证明了结论。

发现 fk=max(i+si+1k) 可以用斜率优化,于是就可以做到 O(n)

1|9P6047 丝之割

题意:有若干条形如 (u,v) 的弦,可以进行若干次操作,每次操作选定 (i,j),会切断所有满足 u>i,j<v 的弦会被破坏,代价为 ai×bj,求破坏所有弦的最小代价。

思路:先考虑那些弦不会做贡献,很容易发现如果有两个弦 i,j 满足 uj>ui,vj<vi 那么如果要删弦i就一定会同时删弦 j,那么弦 j 就不做贡献。当我们把所有不做贡献的弦 j 删去后,就不会存在一条弦的 u 大于另一条弦的 uv 较小,换而言之就是弦的 u,v 都是单增的有了这个性质就很好DP了。

fi 为破环前 i 个弦的最小代价,转移方程为 fi=minj=1i1(fj+mink=1uj1ak×mink=vi+1nbk)

朴素的转移是 O(n2) 的,我们考虑优化转移。我们发现,这个转移的形式似乎可以斜率优化,把 fj 当成 y,把 mink=1uj1ak 当成x,把 mink=vi+1nbk 当成 k,就和斜优的形式一模一样了,就可以愉快的套板子了。复杂度 O(n)

trick:斜率优化。

1|10CF1188C Array Beauty

题意:定义一个序列的权值为两个数之差的绝对值的最小值,求长为n的序列a的所有长为k的子序列的权值之和

思路:首先肯定是对 a 数组排序。我们发现,这一题的值域很小,那做法可能和值域有关。答案的最大值肯定不会超过 ank1,因此我们考虑枚举答案 v,然后 DP 计算权值 v 的方案数,然后直接将方案数相加即可。然后设 f[i][j] 表示当前在第 i 个数且选第 i 个数,已经选了 j 个且满足条件的方案数,所以 f[i][j]=aiakvf[k][j1],然后用前缀和优化就是 O(nk) 的了。总复杂度 O(Vknk)=O(nV)

trick:分析数据范围,斜率优化。

1|11CF1179D Fedor Runs for President

题意:在树上删除一条边后,求最多能形成多少条简单路径。

思路:树上斜率优化。

首先只考虑加一条边后会多出来多少条路径。假设加入的边为 (x,y),设 skpath(x,y) 上一点 k 不向路径延伸的子树大小,那

ans=kpath(x,y)sk(nsk)2

=n×kpath(x,y)skkpath(x,y)sk22

=n2kpath(x,y)sk22

要让答案最大,我们就要选一条路径使 kpath(x,y)sk2 尽量小。

我们设 x 为当前点,然后计算每一条经过 x 的路径的答案。设 fxx 的子树中让上式最小的路径,则 fx=minyson(x)fy+(sizexsizey)2 其中 sizexsizey 即为 sx

求出这个后,考虑如何将两条路径合并。先朴素直接枚举两个儿子来转移,即

ans=minu,vson(x),uvfu+fy+(nsizeusizev)2

这样一次转移是 O(sonx2) 的,但看到这个二次式我们尝试用斜率优化。我们枚举一个 u,然后求出最优决策点 v。枚举两个点 i,j 假设 ij 更优,那么有

fu+fi+(nsizeusizei)2fu+fj+(nsizeusizei)2

化简后可得

fi+sizei22(nsizeu)sizeifj+sizej22(nsizeu)sizej

再变形可得

(fi+sizei2)(fj+sizej2)2(nsizeu)(sizeisizej)

因此当 sizei>sizej 时,

(fi+sizei2)(fj+sizej2)(sizeisizej)2(nsizeu2)

然后再注意 sizei<sizej 时不等号变号,以及特判一下等于即可。还有就是我们按 size 大小先给所有儿子排序一遍后转移就很方便了。

总复杂度O(nlog(n))

trick:树上斜率优化。

1|12P4292 [WC2010] 重建计划

题意:求有上下界树上平均值最大路径。

思路:有点分治的做法,感觉长剖比较好写,就来写长剖了。

对于每个点,设 f[x][i] 表示点 x 向下长度为 i 的链的最大权值,用长链剖分 + 线段树优化就可以做到 O(nlogn)

1|13CF671D Roads in Yusland

题意:给定一棵 n 个点的以 1 为根的树。有 m 条路径 (x,y),保证 yxx 的祖先,每条路径有一个权值。你要在这些路径中选择若干条路径,使它们能覆盖每条边,同时权值和最小。

思路:左偏树优化 DP。

f[x] 表示覆盖了 i 子树及 i 连向 fa[i] 的边的最小代价,我们发现转移时无法只保留最优的,因为我们的选择是有后效性的,但是可以选择的链总共只有 O(n) 个,于是可以考虑保留所有可能的最优方案,用左偏树来维护,转移时有全局加,合并,删除,都可以维护。复杂度 O(nlogn)

trick:如果发现状态中维护的不能只有答案,可以先保留所有对后面有贡献的方案,然后再想办法优化。

1|14CF809D Hitchhiking in the Baltic States

题意:给出 n 个区间 [li,ri]n 个未知数 a1,a2,,an,现在你要确定这 n 个数,使得 ai[li,ri],并且这个序列的最长严格上升子序列尽可能大,求这个最大值。

思路:设 dp[i] 表示要让最长上升子序列长度为 i 时子序列最后一个的最小值。

考虑转移:

  1. dp[i1]<l,那么 dp[i]l
  2. dp[i1][l,r1],那么 dp[i]dp[i1]+1
  3. dp[i1]r,那么不变。

暴力转移是 O(n2) 的,考虑用平衡树优化。

  1. 用小于 l 的最大的 dp[i] 转移到 dp[i]+1
  2. 让区间 [l,r1] 内的 +1,然后往后移。
  3. 删掉大于等于 r 的最小的数。

trick:如果转移形式简单,可以把相同转移的一起处理。

1|15CF856F To Play or not to Play

思路:考虑一个最暴力的做法,设 dp[i][a][b] 表示前 i 个时段,V 的经验为 a,P 的经验为 b 是否可行。

考虑怎么简化状态。我们发现,如果状态 (a,b) 合法,那么 (a1,b),(a,b1) 合法,于是我们只需要记录所有不存在 (a,b) 满足 aa,bb(a,b) 即可。

考虑现在加入了一个时段,长度是 len

如果只有 V 在线,那么所有的 (a,b) 都会变成 (a+len,b)

如果只有 P 在线,那么所有的 (a,b) 都会变成 (a,b+len)

如果两人都在线,那么分 3 种:

  1. |ab|C,那么会变成 (a+2len,b+2len)
  2. ab>C,那么可以让两个人都上线变成 (a+len,b+len),或者让 a 减小到 b+C,那么会变成 (b+C+2len,b+2len)
  3. ba>C,同上。

考虑用平衡树维护。

对于前两种,就是全局的 a 增加或者 b 增加。

对于后一种,我们把平衡树分成 ab>C,|ab|C,ba>C 三部分,对于第一个部分,我们把 b 最小的一组变成 (b+C,b) 然后加入第二部分,第三部分同理,然后合并时去掉会被二维偏序掉的状态即可。

trick:如果状态之间存在偏序关系,可以把被偏序掉的状态去掉,减少状态量;如果转移形式简单,可以把相同转移的一起处理。

1|16CF1630D Flipping Range

题意:给定一个长度为 n 的数组 a 和一个包含 m 个正整数的集合 b,保证对于所有的 1im,都有 1bin2。你可以对 a 数组执行若干次操作,每次操作中,你可以:

  • 选出一个在 b 集合中出现过的数 x
  • 选择 a 数组中一个长度为 x 的子段,然后将该子段里的所有元素的值变为其相反数。形式化地来说,选出满足 1lrnrl+1=x 的两个数 l,r,然后对于所有的 lir,将 ai 替换为 ai

你希望最大化 i=1nai 的值,求这个最大值。

思路:好厉害的题目。

首先,如果 2kn,那么答案就可以取到下界 n,于是现在的 k 降到了 O(logn) 的量级。设 f[n][k] 表示答案,那么转移就是 dp[n][k]=mini=0n1dp[i][k1]+c(i+1,n)

考虑怎么求 c(l,r)

c(l,r)=i=lrj=ir[gcd(i,j)l]=i=lrj=irk=lr[gcd(i,j)=k]=k=lri=1rkj=irk[gcd(i,j)=1]=k=lri=1rkφ(i)

S(i)=j=1iφ(j),于是可以整除分块在 O(rl) 的时间内计算 c(l,r)

然后有 c(l,r) 满足四边形不等式。

证明:记 f(l,r,k)=i=lrS(ki),那么对于 ijkl,有:

c(i,l)+c(j,k)=f(i,l,l)+f(j,k,k)=f(i,j1,l)+f(j,l,l)+f(i,k,k)f(i,j1,k)=c(i,k)+c(j,l)+f(i,j1,l)f(i,j1,k)

因为有 kl,因此 f(i,j1,l)f(i,j1,k),于是有 c(i,k)+c(j,l)c(i,l)+c(j,k)

然后就可以用分治解决,复杂度 O(nlog2nn)

不过可以做到 O(nlog3n)。类似 P5574 [CmdOI2019] 任务分配问题,可以考虑维护端点 l,r 然后在每次移动时计算贡献。左端点的移动是 O(1) 的,右端点移动时,要对满足 r1k<rkk 将贡献加上 φ(rk),而这样的 k 一定是 r 的因数,于是复杂度就是 O(nlog3n) 的了。

1|17CF1119F Niyaz and Small Degrees

题意:一棵大小为 n 的树,有边权,设 f(x) 表示要满足所有点的 degx 所要删掉的边的边权和的最小值,求出 f(0)f(n)

思路:先考虑对于每个 x 计算答案。设 dp[i][0/1] 表示 i 向上连的边删或不删时的最小代价。转移时,对于 i 的每个儿子 j,有两种贡献,a1=dp[j][0]+w 表示删掉 (i,j) 的贡献,a2=dp[j][1] 表示不删 (i,j) 的贡献。我们考虑先取所有的 a2,用堆来维护所有的 a1a2,然后选最小的一些a1a2a2 替换掉,这一步可以用堆维护。单次复杂度 O(nlogn)

然后考虑正解。如果从小到大考虑每个 x,那么一个 deg=x 的点对 [x,n1] 是没有贡献的,对于每个点只需考虑 x<deg 的情况,这样的量级是 deg=n 的。我们将 degx 的点视为无用点,其他为有用点,然后从每个有用点开始 dfs,把无用点视为叶子,这时每个无用点的 a1=w,a2=0,它对它相邻的有用点的贡献即为 a1a2=w,然后像暴力做法一样加入有用点的贡献,最后再加上一直弹堆顶到堆中元素为须删掉的边数时,堆中所有 a1a2 的和就是答案。注意更新答案后要撤销加入的有用点的贡献,并撤销不断弹出堆顶直到堆中元素符合要求时删除的无用点的贡献,需要用可删除堆维护。总(均摊)复杂度 O(nlogn)

1|18P1295 [TJOI2011] 书架

题意:给一个序列 h,要分成若干段使得每一段长度不大于 m,最小化所有段的最大值之和。(其实这题本来不是这个题单里的)

思路:首先可以很简单的想到一个 O(n2) 的 DP:设 fi 表示 i 为当前段末尾的最小和,则 fi=minsisj<=m(fj+maxk=j+1ihk)。因为有最大值,我们考虑找找单调性。首先,如果 hjhj+1,那么 maxk=jihk=maxk=j+1ihk,而 f 显然是单调递增,那这个时候从 j 转移一定比从 j+1 转移更优,所以我们很容易用一个单调队列(记为 q)来记录可能对答案做贡献的位置。然后我们考虑如何计算贡献。因为队列里的值是递减的,即 hql>hqr,那么每个的贡献就是 fql+aql+1,这时我们还需维护 f。为了保证复杂度,我们可以用两个单调下降的栈来维护 f,每当左或右端点到了中点就重构,这样复杂度就是 O(n) 的了。

1|19[ARC082F] Sandglass

题意:有一个沙漏,每秒会有 1g 沙子从上面落下来,有 k 个时刻 r1,r2rk,每到 ri 沙漏就会翻转,多次询问如果一开始上部有 aig 沙子,在时刻 ti 时上部有多少沙子。

思路:一开始的思路是每个操作形如 xmax(xΔr,0),可以按时间轴用线段树维护答案,没想到这题竟然可以做到线性。

其实线性做法的思路也是维护如果一开始是多少此时就有多少,记为 fi,那么可以把 fi 的图像画出来,发现就像这样img最终值不和上下界(红线)相等的 fi 必然满足在一个区间内,因此我们只用这个区间的左右端点维护上下界即可做到线性。

trick:通过画图分析转移的性质。

1|20P2519 [HAOI2011] problem a

题意:一次考试共有 n 个人参加,可能出现多个人成绩相同的情况。第 i 个人说:“有 ai 个人成绩比我高,bi 个人成绩比我低。”

请求出最少有几个人没有说真话。

思路:发现条件等价于 我是第 ai+1 名,有 naibi 个人和我的分数相同,相当于是将分数从小到大排序后,与第 i 个人分数相同的区间是 [ai+1,nbi]

如果超过 rl+1 个人的区间都是 [l,r],那么最多只有 rl+1 个人满足条件。

如果把有多少人的区间是 [l,r] 当成区间 [l,r] 的贡献,那么我们要求的就是选择若干不交区间,求最大代价。

f[i] 是前 i 个区间的最优解,转移时枚举最大的 j 满足 rj<li,有 f[i]f[j]+val[i]

复杂度 O(nlogn)

1|21P9523 [JOISC2022] 复制粘贴 3

题意:构造长度为 n 的目标串,初始 X 为空,支持三种操作:

  • 输入 c,即 XX+c
  • 剪切,将 X 剪切到剪切板 Y,并令 X 为空。
  • 粘贴,即 XX+Y

分别有代价 A,B,C,求得到目标串的最小代价。

思路:设 f[l][r] 表示当前区间的答案,那么转移有两种,一种是 f[l][r]=A+f[l][r1],一种是选择 k,然后将 s[l,r] 中删掉尽量多的 s[l,k]

直接转移复杂度很劣,考虑怎么优化。我们一加上 f[l][r]=A+f[l+1][r],这样我们进行剪切操作的转移就可以钦定在 s[l,p]=s[q,r] 时才进行。

预处理出 kmp 数组,然后每次跳 next 来转移,这样做是 O(n2) 的。

现在还需快速求出 [l,r] 中可以选出多少个 [l,p]。可以预处理出倍增数组 g[l][r][k] 表示和 s[l,r] 相同的不重复的后面 2k 个子串的右端点,然后就可以快速求了。

复杂度 O(n2logn)

1|22LOJ#6564. 最长公共子序列

题意:求 LCS。

思路:一直没怎么做过bitset科技题。

朴素求LCS的DP是 O(nm) 的,但是我们发现 DP 的差分数组 c[i][j]=dp[i][j]dp[i][j1] 只有 0/1 两种取值,这启发我们用 bitset 来优化。考虑加入 ai+1 时,对于原来差分数组的每一段极长连续段满足 c[i][l],c[i][l+1]c[i][r1]=0,c[i][r]=1,我们找到最左的满足 bj=ai+1,于是有 v[i+1][j]=1,v[i+1][r]=0,如果找不到就不变。

考虑怎么用 bitset 来进行操作。

f 表示当前的差分数组,g 表示 f<<1f 表示在 f 所有匹配位置变成了 1 后的数组,那么令 g=fg,再 ff&(fg) 即可。

trick:如果 DP 数组的差分只有 0/1 可以考虑用 bitset 来优化转移。


__EOF__

本文作者Xttttr
本文链接https://www.cnblogs.com/Xttttr/p/18016571.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   Xttttr  阅读(42)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
点击右上角即可分享
微信分享提示