中等水平各类dp解题报告

中等水平各类dp解题报告

  1. 前言

最近退化了,做题养生

中等水平各类dp

updated at 20:16 on 2024/11/8


P4310 绝世好题

考虑 \(f_i\) 表示序列 \(a_{1 \cdots i}\) 的最长子序列长度,以 \(i\) 结尾。

转移就是 \(f_i = \max_{j=1}^{i-1} f_j + 1\), 要求 \(a_i \& a_j \neq 0\)

时间复杂度 \(O(n^2)\)

优化肯定在于 \(a_i \& a_j \neq 0\) 这个条件

考虑 \(a_i\) 的每一个为 \(1\) 的二进制位 \(k\), 只要 \(a_j\)\(k\) 位也为 \(1\) 即可。

\(g_i\) 维护每一位为 \(1\) 的最大\(f_i\) 辅助转移即可。

时间复杂度 \(O(n\log A)\)


P5322 排兵布阵

背包模型。预处理第 \(i\) 场投入 \(j\) 人的收获总分 \(w_{i,j}\),套上一个分组背包即可。

时间复杂度 \(O(ns(m+\log s))\)


UVA10534 Wavio Sequence

题意:找出最长严格单峰子序列。

考虑保存前缀最长严格递增子序列与后缀最长严格递减子序列的长度,枚举中间那个峰统计答案即可。

时间复杂度 \(O(n\log n)\)

没法提交代码,放这里

跳过代码

#include<bits/stdc++.h>
using namespace std;
#define fr(i,a,b) for(int i=a;i<=b;i++)
#define ll long long
#define db double
#define mod 998244353
#define N 10005
int n,cnt,a[N],b[N],f[N],g[N];
int main(){
	while(scanf("%d",&n)!=EOF){
		fr(i,1,n)scanf("%d",&a[i]);
		cnt=0;
		fr(i,1,n){
			if(a[i]>b[cnt])b[f[i]=++cnt]=a[i];
			else b[f[i]=lower_bound(b+1,b+cnt+1,a[i])-b]=a[i];
		}
		cnt=0;
		for(int i=n;i;i--){
			if(a[i]>b[cnt])b[g[i]=++cnt]=a[i];
			else b[g[i]=lower_bound(b+1,b+cnt+1,a[i])-b]=a[i];
		}
//		fr(i,1,n)printf("%d ",g[i]);puts("");
		int ans=0;
		fr(i,1,n)ans=max(ans,(min(f[i],g[i])<<1)-1);
		printf("%d\n",ans); 
	}
	return 0;
}


P1800 software

求解比较难,但是我们发现天最多 \(O(Dm)\),考虑通过二分转化为判定。

如果以 \(T\) 天为DDL,考虑 \(f_{i,j}\) 表示前 \(1\)\(i\) 个人第一个软件在 \(T\) 天内做了 \(j\) 个模块,那么能在 \(T\) 天内第二个软件最多做的模块数量。

这个明显是背包,转移:

\[f_{i,j} = \max f_{i-1,j-k}+(T-kd_1)/d2 \]

时间复杂度 \(O(nm^2\log Dm)\)

应该容易优化掉转移的枚举变成 \(O(nm\log Dm)\)


P1880 石子合并

断环成链的区间DP模板。

\(O(n^3)\)


P1291 百事世界杯之旅

记买到 \(i\) 种名字后还要期望要买 \(f_i\) 瓶才能集齐。有:

\[f_n = 0 \\ f_i = 1 + \dfrac in f_i +\dfrac {n-i}n f_{i+1} \]

这是因为集齐 \(i\) 个后再买一个,有 \(\dfrac in\) 的概率买到重复的,有 \(\dfrac {n-i}n\) 的概率买到新的。

解得:

\[answer = f_0 = \sum_{i=1}^n n/i \]

然后就是高精度模板。


P1004 方格取数

关键解决去重,由于只能向右向下移动,我们同时维护两条路径,记 \(f_{x,y,z,w}\) 表示第一条路径走到 \((x,y)\),第二条走到 \((z,w)\) 的最大得分。枚举两个走的方向转移,注意如果走到了同一个点只能取一次。

时间复杂度\(O(n^4)\)

观察到只有 \(x+y=z+w\) 的点有意义,转而维护 \(f_{x+y,x,z}\)

时间复杂度\(O(n^3)\)


P4170 涂色

简单分析后发现覆盖操作之间相互包含或相离,反正不会是相交(肯定亏了)。

于是考虑区间DP,\(f_{l,r,c}\) 表示当前 \([l,r]\) 区间都为颜色 \(c\),还需要染色的最小次数。

\(a_l=c\)\(f_{l,r,c} = f_{l+1,r,c}\)

否则 \(f_{l,r,c} = 1+\min_{p=l}^r f_{l+1,p,a_l}+f_{p+1,r,c}\)

时间复杂度\(O(n^3C)\)


从第三部分开始,前面太水了。。


CF1107E

区间DP。若只记 \(f_{l,r}\) 可能有一些拼接区间的操作无法顾及到。

考虑到拼接可以认为是一个个接上去的,记 \(f_{l,r,k}\) 表示删掉 \([l,r]\)\(k\) 个额外 \(s_r\) 的最大得分。

转移只有: 直接删掉 \(s_r\) 及后面的东西,或者枚举一个 \(p\)\(s_p=s_r\),删掉 \([p+1,r-1]\) 从而拼接 \(s_r\)

时间复杂度\(O(n^4)\)


P2886 Cow Relays G

矩阵快速幂模板,点太多离散化即可。

\(O(T^3\log n)\)


P2151 HH去散步

HH是变化之神。

关键是处理“不会立刻沿着刚刚走来的路走回”的限制。

使用点边互换技巧。等价于找到边的长为 \(T\) 的序列使第一条边起点为 \(S\),最后一条边终点为 \(E\),任一条边终点为下一条边起点。构建边链接关系的有向图,一条边终点为另一条边起点就给它们连边,但是一条边不能连接其反边。

矩阵快速幂,与上题的 \((\max,+)\) 矩阵不同,这题是 \((+,*)\) 矩阵。

\(O(m^3\log t)\)


P2279 消防局的设立

\(f_{u,(0/1)*6}\) 表示处理了以 \(u\) 为根的子树,\(u\) 有没有消防局,\(u\) 有没有被消防局覆盖,\(u\) 的儿子有没有存在消防局,\(u\) 的儿子有没有全被消防局覆盖,\(u\) 的孙子有没有存在消防局,\(u\) 的孙子有没有全被消防局覆盖,这种情况下的最小消防局数,转移即可。

\(O(n)\),但是有 \(2^{12}\) 的巨大常数,其实可以优化


P4441 Retro

下落等价于人向上走,考虑普通的DP就是 \(f_{i,j,k}\) 表示当前在 \((i,j)\) 上拥有 \(k\) 个左括号,最多有几个括号对已经确定。转移枚举方向,简单做到 \(O(n^3)\),还原方案记录决策点即可。

问题在于选出字典序最小的方案,直接用一个$ \leq 2^{300}$ 的大数来存储并比较当前的字典序即可。时间复杂度 \(O(\dfrac{n^4}w)\),不需要记录决策点,滚动优化空间,空间复杂度 \(O(\dfrac{n^3}w)\)

有大病 \r 换行


P6847 [CEOI2019] Magic Tree

不在题单里,但我刷到觉得比较精彩。

发现一条边断掉后它对应的子树就废了,所以不如断掉它同时断掉子树内的所有边,于是问题转化为每个果实选择收获或者不收获,但是收获时间自下往上必须单调不减,问总共最大价值。

DP,\(f_{u,i}\) 表示 \(u\) 子树内选择的收获时间不超过 \(i\),子树内总共最大价值。

合并 \(u\)\(v\) 就是:

\[f_{u,i} \leftarrow f_{u,i} + f_{v,i} \]

最后考虑 \(u\) 可能要选:

\[f_{u,j} \leftarrow \max (f_{u,j} , f_{u,d_u}+w_u) \]

直接来时空 \(O(n^2)\),34pts

看前面那条转移容易想到线段树合并。处理后面那条转移需要标记永久化,合并时标记也相加。比较麻烦,可以看题解。

时空 \(O(n\log n)\)


P5785 任务安排(批量计划)

用到一个经典技巧:费用提前计算

\(f_i\) 表示做完前 \(i\) 个任务的费用最小值,但是提前考虑一个任务对后面时间累积的影响。比如做了第一个任务的贡献为 \(t_1\cdot (c_1+c_2+\cdots+c_n)\),因为做了它会导致后面每个任务的完成时间推迟 \(t_1\)。由此得到转移:

\[f_i = \min_{j=1}^{i-1}\quad (s+sT_i-sT_j)\cdot bc_j+f_j \]

其中 \(sT\)\(T\) 的前缀和,\(bc_i = \sum_{j>i}c_j\)

斜率优化即可。

还是推一下式子吧

\(y_j = (s-sT_j)\cdot bc_j+f_j\)

即对于 \(i\)\(j\) 使得 \(sT_i\cdot bc_j + y_j\) 最小

\(sT_i\cdot bc_j + y_j<sT_i\cdot bc_k + y_k\) 则 $-sT_i< \dfrac{y_k-y_j}{bc_k-bc_j} $

(假设 \(bc_k>bc_j\))

这说明要维护一个下凸包,不断在左侧加点。查询时二分斜率。

时间复杂度\(O(n\log n)\)


P3188 梦幻岛宝珠

看起来01背包无法时间复杂度少于 \(O(nm)\),其实关键在于体积的形式一定是\(a\times2^b\)

发现这样的关键性质是 \(b\) 越大的就天然地比较大一些,定量地描述就是如果手上有 \((\max a)\times2^b\) 的容量则体积形如 \(a\times 2^{b-1}\) 的物品一定能买。如果有 \(n(\max a)\times2^b\) 的容量则 \(b\) 更小的物体都能扫荡了。

基于此,可以设计一个DP \(f_{i,j}\) 表示考虑了所有 \(b\ge i\) 的物品,手上还剩下 \(j\times 2^i\) 容量的最大价值。同一层转移就是考虑所有 \(b=i\) 的物品选不选,正常转移即可。往下转移就是 \(j \leftarrow j\times2 + W[j]\),其中 \(W[j]\) 代表 \(W\) 的二进制第 \(j\) 位。如果过大则能扫荡,体积设为 \(n(\max a)\) 即可。

状态数及向下转移复杂度为 \(O(B\times nA)\),每个物品转移需要 \(O(nA)\),时间复杂度为 \(O(T(n+B)nA)\)


P2943 Cleaning Up G

一看像是决策单调性,一推不满足四边形不等式qwq

发现有一个性质,如果分出一组有 \(i\) 种元素,那这组一定起码要有 \(i^2\) 个元素才能不亏。所以 \(i>\sqrt n\) 都亏了。于是枚举转移点改为枚举当前区间有多少种元素,再算出这时左边最多能去到哪里即可转移。

问题变成如何快速确定左边界。这也好办,因为只需要维护 \(O(\sqrt n)\) 个左端点,维护所有指针暴力移动即可。

时间复杂度变为 \(O(n\sqrt n)\)


posted @ 2024-11-01 21:09  Vizing  阅读(17)  评论(0编辑  收藏  举报