一、背包
「HNOI2007」梦幻岛宝珠
题目
将物品按照
b
b
b 分组,每组内部 dp,然后合并。
组内的 dp,就是设
g
[
i
]
[
j
]
g[i][j]
g[i][j] 第
i
i
i 组,选出
j
×
2
i
j\times2^i
j×2i 的最大价值。
现在考虑如何合并,设
f
[
i
]
[
j
]
f[i][j]
f[i][j] 从高到低到
i
i
i 位,
w
w
w 减去已选后还剩
j
×
2
i
j\times 2^i
j×2i 的最大价值。
转移是
f
[
i
−
1
]
[
2
×
j
+
c
−
k
]
←
f
[
i
]
[
j
]
+
g
[
i
−
1
]
[
k
]
f[i-1][2\times j+c-k]\leftarrow f[i][j]+g[i-1][k]
f[i−1][2×j+c−k]←f[i][j]+g[i−1][k]。需要注意的是,转移中的
2
×
j
+
c
−
k
2\times j+c-k
2×j+c−k 要与
10
×
n
10\times n
10×n 取 min,因为过大的值即使记录下来也对后续无影响。
「NOI2015」寿司晚宴
题目
首先考虑暴力。设
d
p
[
i
]
[
S
1
]
[
S
2
]
dp[i][S_1][S_2]
dp[i][S1][S2] 表示前
i
i
i 个数,两个人选择的质数集合分别是
S
1
S_1
S1,
S
2
S_2
S2。转移就是
d
p
[
i
+
1
]
[
S
1
∣
k
]
[
S
2
]
←
d
p
[
i
]
[
S
1
]
[
S
2
]
dp[i+1][S_1|k][S_2]\leftarrow dp[i][S_1][S_2]
dp[i+1][S1∣k][S2]←dp[i][S1][S2] 和
d
p
[
i
+
1
]
[
S
1
]
[
S
2
∣
k
]
←
d
p
[
i
]
[
S
1
]
[
S
2
]
dp[i+1][S_1][S_2|k]\leftarrow dp[i][S_1][S_2]
dp[i+1][S1][S2∣k]←dp[i][S1][S2]。
n
≤
500
n\le500
n≤500,一个数中大于
n
\sqrt{n}
n 也就是
22
22
22 的质因数只会出现一个。基于上面的暴力做法,我们先记录每一个数中的大于
22
22
22 的质因数(如果没有就可以直接用暴力做法做),然后将大质数一样的一起做。
第二遍做还是不太会。
- 分解质因数想到大小质数分开处理是自然的。这题用到的结论是:一个数含有的大质数只有一个,所以拥有同样的大质数的数可以一块处理,而没有大质数的数再放一起处理。
- 想这道题的时候总是想枚举质因数,然后发现需要容斥。但是注意到 n n n 很小,枚举数字就可以解决。
二、排列 dp
「CEOI2016」kangaroo
题目
首先一步转换,就是转换成对排列
p
p
p 计数,要求
∀
i
\forall i
∀i,
p
i
−
1
,
p
i
+
1
p_{i-1},p_{i+1}
pi−1,pi+1 同时小于或大于
p
i
p_i
pi。
设
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j] 表示放了
i
i
i 个数,分成
j
j
j 段的方案数。然后转移分成三种情况:新增一块;合并两块;在一块后加数。
第三种情况是肯定无法满足前面的要求的,所以我们只考虑前两种情况。
- 新增一块。 f [ i ] [ j ] = f [ i − 1 ] [ j − 1 ] × j f[i][j]=f[i-1][j-1]\times j f[i][j]=f[i−1][j−1]×j
- 合并两块。 f [ i ] [ j ] = f [ i − 1 ] [ j + 1 ] × j f[i][j]=f[i-1][j+1]\times j f[i][j]=f[i−1][j+1]×j
另外需要特判一下 i = s i=s i=s 和 i = t i=t i=t 的情况,以及如果 i > s i>s i>s 时不能放在开头和 i > t i>t i>t 时不能放在结尾处。
「JOI Open 2016」摩天大楼
题目
设
d
p
[
i
]
[
j
]
[
k
]
[
0
/
1
]
[
0
/
1
]
dp[i][j][k][0/1][0/1]
dp[i][j][k][0/1][0/1] 表示从大到小加入
i
i
i 个数,有
j
j
j 个连续段,目前题面中的式子的值是
k
k
k,第一个数是否加入,最后一个数是否加入。
我们计算贡献时可以考虑类似扫描线的方式计入贡献,也就是新加入一个数时增加
(
a
i
+
1
−
a
i
)
×
段数
(a_{i+1}-a_i)\times \text{段数}
(ai+1−ai)×段数 的值。但是开头结尾不会造成贡献,这也是我们新增后两维的原因。
难写难调。
三、状压 dp
CF11D A Simple Task
题目
很容易想到一个 时间
O
(
2
n
×
n
3
)
O(2^n\times n^3)
O(2n×n3),空间
O
(
2
n
×
n
2
)
O(2^n\times n^2)
O(2n×n2) 的做法,就是设
f
[
s
]
[
u
]
[
v
]
f[s][u][v]
f[s][u][v] 表示选择点集为
s
s
s,形成了以
u
u
u 开始
v
v
v 结尾的链;
g
[
s
]
g[s]
g[s] 表示以点集
s
s
s 形成的环的方案数,最后做一个高维前缀和即可。但是显然时空复杂度多了个
n
n
n。考虑将状态中的
u
u
u 去掉,变成钦定
s
s
s 中的一个
1
1
1 位为
u
u
u。
「CEOI2019」Amusement Park
对 DAG 计数可以考虑每次向点集中加入一些度为 0 0 0 的点,但是同样会出现算重。
设
f
[
s
]
f[s]
f[s] 包含表示点集的 DAG 数。转移就是枚举当前 DAG 中度为
0
0
0 的点集
t
t
t,然后
f
[
s
]
←
f
[
s
⊕
t
]
f[s]\leftarrow f[s\oplus t]
f[s]←f[s⊕t]。
但是当前加入的度为
0
0
0 的集合不一定都会向上一次加入的度为
0
0
0 集合连边,所以会算重。所以转移就是
f
[
s
]
=
∑
t
⊆
s
(
−
1
)
∣
t
∣
+
1
f
[
s
⊕
t
]
f[s]=\sum_{t\subseteq s} (-1)^{|t|+1}f[s\oplus t]
f[s]=∑t⊆s(−1)∣t∣+1f[s⊕t]。时间复杂图
O
(
3
n
)
O(3^n)
O(3n)。
四、树形 dp
「NOIP2022」建造军营
题目
由于如果两点属于同一个边双,那么不管删那一条边都不会影响两点的连通性,所以首先边双缩点。
边双缩点后的图是一棵树。
考虑树形 dp。设
f
[
u
]
[
0
/
1
]
f[u][0/1]
f[u][0/1] 表示
u
u
u 子树中,
u
u
u 放不放的方案数。
f
[
u
]
[
0
]
=
f
[
u
]
[
0
]
×
f
[
v
]
[
0
]
f[u][0]=f[u][0]\times f[v][0]
f[u][0]=f[u][0]×f[v][0]
「KDOI-06-S」树上异或
题目
首先拆位。
设
f
[
u
]
f[u]
f[u] 表示以
u
u
u 为根的子树的答案,
g
[
u
]
[
i
]
[
0
/
1
]
g[u][i][0/1]
g[u][i][0/1] 表示当
u
u
u 所在的连通块二进制第
i
i
i 位是
0
/
1
0/1
0/1 时,以
u
u
u 为根的子树中去掉
u
u
u 所在的连通块后的答案。
g
[
u
]
[
i
]
[
0
]
=
g
[
u
]
[
i
]
[
0
]
×
f
[
v
]
+
g
[
u
]
[
i
]
[
0
]
×
g
[
v
]
[
i
]
[
0
]
+
g
[
u
]
[
i
]
[
1
]
×
g
[
v
]
[
i
]
[
1
]
g[u][i][0]=g[u][i][0]\times f[v]+g[u][i][0]\times g[v][i][0]+g[u][i][1]\times g[v][i][1]
g[u][i][0]=g[u][i][0]×f[v]+g[u][i][0]×g[v][i][0]+g[u][i][1]×g[v][i][1]。
g
[
u
]
[
i
]
[
1
]
=
g
[
u
]
[
i
]
[
1
]
×
f
[
v
]
+
g
[
u
]
[
i
]
[
1
]
×
g
[
v
]
[
i
]
[
0
]
+
g
[
u
]
[
i
]
[
0
]
×
g
[
v
]
[
i
]
[
1
]
g[u][i][1]=g[u][i][1]\times f[v]+g[u][i][1]\times g[v][i][0]+g[u][i][0]\times g[v][i][1]
g[u][i][1]=g[u][i][1]×f[v]+g[u][i][1]×g[v][i][0]+g[u][i][0]×g[v][i][1]。
f
[
u
]
=
∑
i
=
0
60
g
[
u
]
[
i
]
[
1
]
×
2
i
f[u]=\sum_{i=0}^{60} g[u][i][1]\times 2^i
f[u]=∑i=060g[u][i][1]×2i。
「HEOI2013」SAO
题目
有点像这题。所以我受这题的影响一直在想边拓扑排序边转移 dp。
但是我们可以考虑一个问题,就是把题目给定的图的有向边变成无向边,原图就是一棵树,正好方便我们树形 dp。所以小改状态,设
f
[
u
]
[
i
]
f[u][i]
f[u][i] 表示以 u 为根的子树中,
u
u
u 的拓扑序为
i
i
i 的方案数。暴力转移是
O
(
n
3
)
O(n^3)
O(n3),前缀和优化后就变成
O
(
n
2
)
O(n^2)
O(n2)。
具体可以看代码。
P3647「APIO2014」连珠线
题目
很好很有启发性的一道题。
首先发现蓝线的连接方式要不是
s
o
n
1
→
u
→
s
o
n
2
son_1\rightarrow u\rightarrow son_2
son1→u→son2,要不就是
f
a
t
h
e
r
→
u
→
s
o
n
father\rightarrow u\rightarrow son
father→u→son。按照这样写一个树上背包,然后发现错了。因为加入蓝线时有中间那个点必须后加入的点的要求。所以枚举每一个点作为根后一定有一种选法使得所有蓝线连接方式都是
f
a
t
h
e
r
→
u
→
s
o
n
father\rightarrow u\rightarrow son
father→u→son。根不确定所以换根,首先考虑换跟前的树形 dp。设
f
[
u
]
[
0
/
1
]
f[u][0/1]
f[u][0/1] 表示
u
u
u 子树中,
u
u
u 是否作为蓝线的中点。
f
[
u
]
[
0
]
=
∑
v
∈
s
o
n
(
u
)
m
a
x
{
f
[
v
]
[
0
]
,
f
[
v
]
[
1
]
+
w
}
f[u][0]=\sum_{v\in son(u)} max\{f[v][0],f[v][1]+w\}
f[u][0]=∑v∈son(u)max{f[v][0],f[v][1]+w}。
f
[
u
]
[
1
]
=
f
[
u
]
[
0
]
+
m
a
x
v
∈
s
o
n
(
u
)
f
[
v
]
[
0
]
+
w
−
m
a
x
{
f
[
v
]
[
0
]
,
f
[
v
]
[
1
]
+
w
}
f[u][1]=f[u][0]+max_{v\in son(u)} f[v][0]+w-max\{f[v][0],f[v][1]+w\}
f[u][1]=f[u][0]+maxv∈son(u)f[v][0]+w−max{f[v][0],f[v][1]+w}。
换根就是本质就是加入不在当前点的子树的贡献,换言之就是加入
f
a
t
h
e
r
[
u
]
father[u]
father[u] 子树且除去
u
u
u 子树的贡献加上不在
f
a
t
h
e
r
[
u
]
father[u]
father[u] 子树中的贡献。那么我们还要在第一次树形 dp 时记录
u
u
u 子树中除去儿子
v
v
v 子树的贡献后的贡献。并且还要记录计算
f
[
u
]
[
1
]
f[u][1]
f[u][1] 时减去的
m
a
x
{
f
[
v
]
[
0
]
,
f
[
v
]
[
1
]
+
w
}
max\{f[v][0],f[v][1]+w\}
max{f[v][0],f[v][1]+w} 的值,按照套路记录次大值防止
v
v
v 正好是删掉的那个儿子。
而换根时加上的
f
a
t
h
e
r
[
u
]
father[u]
father[u] 子树外的贡献需要在更新
u
u
u 前重新计算
f
[
f
a
t
h
e
r
]
[
0
/
1
]
f[father][0/1]
f[father][0/1] 的答案即可。
细节很多没讲清楚,具体可以看代码。
五、容斥/计数 dp
Grid 2
CF1591F Non-equal Neighbours
「CSP-S2019」Emiya 家今天的饭
已经没有什么好害怕的了
六、区间 dp
CF1863F Divide, XOR, and Conquer
题目
其实感觉这题更偏向优美的性质而非区间 dp 本身。
使区间 [ l , k ] [l,k] [l,k] 异或和大于 [ k , r ] [k,r] [k,r] 的结论是: h i g h b i t ( [ l , r ] ) = h i g h b i t ( [ l , k ] ) highbit([l,r])=highbit([l,k]) highbit([l,r])=highbit([l,k])。
那么就有一个 O ( n 3 ) O(n^3) O(n3) 的区间 dp。考虑优化,记录 L [ i ] L[i] L[i] 表示以 i i i 为左端点的合法区间的 h i g h b i t highbit highbit 有哪些, R [ i ] R[i] R[i] 表示以 i i i 为右端点的情况与前者同理。转移时看 L [ i ] L[i] L[i] 或 R [ i ] R[i] R[i] 中是否有当前的 h i g h b i t highbit highbit 即可。注意考虑 h i g h b i t = 0 highbit=0 highbit=0 的情况(将 h i g h b i t = 0 highbit=0 highbit=0 视为 h i g h b i t = 2 62 highbit=2^{62} highbit=262)。
CF1198D Rectangle Painting 1
题目
设
d
p
[
a
]
[
b
]
[
c
]
[
d
]
dp[a][b][c][d]
dp[a][b][c][d] 表示以
(
a
,
b
)
(a,b)
(a,b) 作为左上角,
(
c
,
d
)
(c,d)
(c,d) 作为右上角的矩形的答案。
转移正常区间 dp 的方法转移即可。
七、神仙方法
CF559E Gerald and Path
题目
从 dp 状态设计到计算重复贡献都很神仙。
d
p
[
i
]
[
j
]
[
0
/
1
]
dp[i][j][0/1]
dp[i][j][0/1] 表示已经给前
i
i
i 条射线定向,其中右端点最靠右的是第
j
j
j 条,它的方向是左/右.
https://www.luogu.com.cn/problem/P4649
https://www.luogu.com.cn/problem/P3624
https://www.luogu.com.cn/problem/P3643
https://www.luogu.com.cn/problem/P4572