省选 dp 专题 3 做题记录
省选 dp 专题 3 做题记录
A CF1326F Wise Men
\(\text{Easy Version}\),\(\text{Hard Version}\)
首先直接计数没有什么特别好的办法,因为这个限制的太死了。考虑到串中连续的一段 \(1\) 代表图上相连的一条链,而如果只考虑链之间的组合应该是比现在要简单的。这样做的问题就是 \(0\) 的限制没有满足,那么考虑容斥,对于串 \(S\) 不考虑 \(0\) 的限制,这样求出的答案实际上应该是 \(S\) 的所有超集的答案之和,也就是说我们只需要再最后做一次 IFMT-and 操作就可以求出正确答案。
现在考虑怎样求出不考虑 \(0\) 限制的答案,此时我们 \(S\) 这个串应该对应把原图划分为若干条链。设 \(S\) 对应的状态把原图划分成的链的长度的可重集合为 \(\{a_1,a_2,\cdots, a_k\}\),则 \(S\) 的答案应该只和这个集合有关。而这个集合的个数实际上就是把 \(n\) 划分成若干正整数之和的方案数,也就是 \(n\) 的划分数 \(P(n)\);而在 \(n=18\) 时 \(P(n)=385\),所以我们可以直接爆搜求出这个集合有哪些元素,求 \(S\) 答案直接找到对应集合看它的答案即可。
现在考虑怎样求一个集合的答案。首先既然是划分成若干条链那么一定要先求出划分链的方案数。设 \(f(i,S)\) 表示当前链结尾为 \(i\),走过的点集合为 \(S\) 的方案数,直接 \(O(2^nn^2)\) dp 是容易的。然后令 \(g(S)\) 表示走过点集为 \(S\) 的链的方案数,那么求某个集合的方案数,实际上就是求出满足 \(|S_i|=a_i\),且 \(S_i\) 两两无交、并为全集的 \(\prod g(S_i)\) 之和。
三个限制显然不好做,考虑到我们有 \(\sum a_i=n\),所以 \(S_i\) 并为全集可以直接推出 \(S_i\) 两两无交,然后第二个限制可以不管。接下来并为全集以及求积的条件很容易让我们想到 FMT-or,而如果没有条件 \(1\) 的话直接做一下 FMT-or 就完了。有了的话其实也不难,原先我们的 \(g\) 数组中每一个 \(S\) 上都有值,现在我们对于一个 \(a_i\),只保留 \(|S|=a_i\) 的 \(S\) 上的值即可。
这样的话我们在爆搜的时候把 FMT 后的 \(g\) 数组的点值乘起来,到最后再做一次 IFMT-or 就能求出并为全集的答案。但实际上由于我们只要全集的答案,所以不需要每次都做 IFMT,直接用容斥 \(O(2^n)\) 求出全集上的值即可。
考虑复杂度,我们对于每一个拆分都要遍历一遍然后累乘一下点值,所以设 \(S(n)\) 表示 \(n\) 的所有分拆数大小之和,复杂度应该是 \(O(S(n)2^n)\) 的。
如此复杂度是 \(O(2^n(S(n)+n^2))\) 的,可以通过。
B CF1119F Niyaz and Small Degrees / P7600 [APIO2021] 封闭道路
\(\text{CF Link}\),\(\text{Luogu Link}\)
首先先来看对于单独的固定的 \(x\) 如何求答案,显然考虑树形 dp,设 \(dp(i,0/1)\) 表示钦定点 \(i\) 与父亲的连边不删除或删除时,让 \(i\) 子树内合法的最小代价。转移是比较好想的,对于一个儿子 \(v\),有两种决策:该边不删除,代价为 \(dp(v,0)\);该边删除,代价为 \(dp(v,1)+w\)。为了让 \(i\) 也满足条件,显然删除的边的数量至少有 \(deg_i-x\),但是对于每个儿子只取最优解并不一定能满足这个要求。因此对于所有取到不删除的点,把代价之差 \(dp(v,1)+w-dp(v,0)\) 扔到一个堆里,然后取最小的加到答案中即可。
这样做单次复杂度为 \(O(n^2\log n)\),无法通过。考虑进一步观察性质,容易发现对于一个 \(x\),\(deg_i\le x\) 的点是完全不用重新 dp 的,称这样的点为无用点。将无用点从树中删去后就形成了若干连通块,每次只需要对连通块 dp 即可。此时再看复杂度,对于一个点 \(i\),它会被处理 \(deg_i\) 次,所以总复杂度是 \(O(\sum deg_i)=O(n)\) 的,那么这个做法是有前途的。
考虑对于以有用点为根进行 dp,然后将无用点视为一个叶子节点,此时这个节点不删除的代价为 \(0\),删除的代价就是边权 \(w\)。按道理来讲此时直接按照上述过程实现就行了,不过实际上不然。因为我们复杂度的正确性是建立在每一次只遍历有用点的基础上的,如果每次还要把无用点扔进堆复杂度是不正确的。
解决方法实际上也比较容易,无用点的贡献一定是不变的,所以对于每个点,其堆中含有的无用点贡献也是不变的。那么我们只需要在一个点变为无用点时向其周围的有用点的堆中加入贡献即可;同时对于每个点,如果把堆中无用点的贡献删掉了,我们也要重新加回来。这个用 set
实现会方便一点,或者用两个堆拼成可删堆实现,复杂度还是 \(O(n\log n)\) 的。
但是还有最后一个问题:如果我们每一次把 set
中最小的 \(deg_i-x\) 个暴力删除求和的话复杂度还是不对,会退化成 \(O(\sum deg_i^2)\)。考虑我们实际上是对 set
中一段前缀求和,可以转化为用总和减去一段后缀的和。而求后缀就给了我们操作空间;在最开始的时候,集合中应该只有无用点,我们先把它的大小压到 \(deg_i-x\) 以内,然后加入有用点,此时要删到只剩 \(deg_i-x\) 个点最多只用有用点个数步。这样的话这一部分的复杂度也变成了有用点的个数之和,总复杂度就是 \(O(n\log n)\) 的了。
C CF1290F Making Shapes
首先容易发现一点:如果每个向量的使用次数 \(a_i\) 确定了,那么最后形成的多边形是唯一确定的。所以我们只需要数出合法的 \(a_i\) 的数量即可。
考虑合法的 \(a_i\) 满足什么条件,第一个条件显然是 \(\sum a_i x_i=\sum a_iy_i=0\),因为要回到原点;第二个条件就是多边形能塞进 \(m\times m\) 的范围内,这个也很好判断,就是 \(\sum a_ix_i[x_i>0]\le m\) 以及 \(\sum a_iy_i[y_i>0]\le m\)。那么实际上我们可以把第一个条件改写一下,变成 \(\sum a_ix_i[x_i>0]+\sum a_ix_i[x_i\le 0]=\sum a_iy_i[y_i>0]+\sum a_iy_i[y_i\le 0]=0\),这样的话条件就只和这四个部分有关了。
不过这个计数貌似还是困难的。注意到 \(n,x_i,y_i\) 的范围都不大,考虑数位 dp 求解。为了方便,把所有操作放到二进制下进行,这样我们我们枚举一个长为 \(n\) 的串就可以知道每一个 \(a_i\) 在当前位上的值。考虑状态,既然我们的条件只和那四个值有关,那就把这些值计入状态,设 \(dp(d,dx,dy,nx,ny,p,q)\) 表示当前枚举到从低到高第 \(d\) 位,钦定第 \(d-1\) 位的 \(\sum a_ix_i[x_i>0]\) 的进位为 \(dx\)、\(\sum a_ix_i[y_i>0]\) 的进位为 \(dy\)、\(\sum a_ix_i[x_i\le0]\) 的进位为 \(nx\)、\(\sum a_ix_i[y_i\le0]\) 的进位为 \(ny\),同时钦定第 \(d-1\) 位及更低位算出的 \(\sum a_ix_i[x_i>0]\) 是否 \(\le m\) 以及 \(\sum a_iy_i[y_i>0]\) 是否 \(\le m\) 的方案数。转移的时候枚举当前位的 \(a_i\) 的值,然后更新状态即可。
初值为 \(dp(t,0,0,0,0,0,0)=1\),最后的答案是 \(dp(0,0,0,0,0,0,0)\)。嫌枚举状态太长的话可以用记忆化搜索实现。由于进位数量最大值为 \(\sum\limits_{i=1}^\infty \lfloor\dfrac{nV}{2^i}\rfloor=nV\),所以总复杂度是 \(O((nV)^4\times 2^n\times \log m)\),可以通过。
D CF1292F Nora's Toy Boxes
首先我们对原序列做一次埃筛,把数字划分到不同集合中,每个集合会有一个最小的数表示集合中所有数都是它的倍数,称这些数为关键点。而显然一个数可能属于多个集合,也就是两个集合会有交集。我们把所有有交集的集合看作一个连通块,对每个连通块单独处理。
可以得到的结论是,每一个连通块内除了关键点和任意一个点外,其他点都能被删掉,原因显然。我们考虑对这个过程进行 dp。删点操作不好完成,考虑改为加点操作,那么一个点 \(x\) 可以被加进去,当且仅当它所属的所有集合中,有一个集合此时的大小 \(\ge 2\)。对集合进行状压,状态为 \(1\) 表示集合大小 \(\ge 2\),可以加点;否则表示该集合只有关键点。
于是设 \(dp(i,S)\) 表示当前加入了 \(i\) 个数,集合状态为 \(S\) 的方案数。令 \(T_i\) 表示 \(i\) 所属的集合构成的集合,转移有两种:
- 当前新加入的数不改变 \(S\),则此时加入的 \(x\) 必然满足 \(T_x\subseteq S\),令 \(cnt\) 表示满足条件的 \(x\) 个数,则转移为 \(dp(i+1,S)\leftarrow dp(i,S)\times (cnt-i)\)。
- 当前新加入的数改变 \(S\),枚举 \(x\) 使得 \(T_x\nsubseteq S\),此时新的状态应该是 \(T_x\cup S\),转移为 \(dp(i+1,T_x\cup S)\leftarrow dp(i, S)\)。
考虑复杂度,发现不清晰的地方只有划分出来的集合数量,也就是关键点数量。首先 \(>30\) 的关键点我们显然不用管,所以只需要看 \(\le 30\) 的关键点。而这些关键点的个数最多只有 \(15\) 个,所以复杂度是 \(O(n^2 2^{15})\) 的。
这个做法已经足够通过,但是还有优化的余地。上面的过程中我们考虑了不改变 \(S\) 的数,实际上我们可以把他们统一放在 \(S\) 改变的时候处理。计算出 \(S\) 改变后新增加的可以放入的数的数量 \(tot\),则这些数可以在后面的任何地方加入,算一个排列数即可。复杂度可以做到 \(O(n 2^{15})\)。
E CF1930G Prefix Max Set Counting
从树的角度去考虑生成这个序列是比较困难的,考虑直接对序列计数,然后判断合法性即可。设 \(dp(i)\) 表示当前以 \(i\) 结尾的前缀 \(\max\) 序列的个数,初值为 \(dp(1)=1\),答案为 \(dp(n)\)。
考虑 \(dp(i)\) 可以从哪里转移,实际上就是看 \(i\) 的上一个点 \(j\) 在哪。分两种情况考虑:在 \(i\) 根链或不在 \(i\) 根链上。
-
如果 \(j\) 在 \(i\) 根链上,显然 \(j\) 应该是 \(i\) 根链上除了 \(i\) 以外最大的点。记 \(w\) 为最大的点的编号,如果 \(w>i\) 则 \(dp(i)=0\);否则 \(dp(i)\leftarrow dp(w)\)。
-
如果 \(j\) 不在 \(i\) 根链上,那么它就应该在根链某个节点的子树中。既然此时的前缀最大值从 \(j\) 跳到了 \(i\),说明 \(j\) 所在的那个子树已经走完了,下一步直接走到 \(i\)。令 \(d=\text{lca}(i,j)\),则此时 \(j\) 必定是 \(d\) 在 \(j\) 方向上的儿子的子树最大值。
但是只考虑到这里还不行,如果 \(j<w\),此时会发现,如果 \(dep_d<dep_w\),则 \(i\) 肯定不是下一个走到的点;而如果 \(dep_d\ge dep_w\),则 \(j\) 根本就走不到。所以这个 \(j\) 的要求是 \(w\le j<i\)。
那么综上所述,满足条件的 \(j\) 要么是 \(i\) 的祖先,要么是祖先的某个儿子的子树最大值,并且 \(w\le j< i\)。后者处理起来还不是特别简单,我们需要对每个点的儿子按照其子树最大值排序,这样遍历一个儿子的子树的时候它一定能接收到第二类点的贡献。剩下的用树状数组维护一下区间和就行了,复杂度 \(O(n\log n)\)。
F P7213 [JOISC 2020] 最古の遺跡 3
拿到题目没有什么可以下手的地方,如果我们按照值域 dp,把值从大往小填肯定是没有前途的;所以我们要在下标上发现一些性质。对于位置 \(i\),我们令 \(i\) 地震结束后高度为 \(b_i\),手玩一下会发现,如果 \(i\) 后面的 \(b_i\) 中有 \(1\sim a_i\) 的所有整数,那么 \(b_i\) 就等于 \(0\);否则的话,它应该是 \(\le a_i\) 的所有 \(b_i\) 中没有出现过的最大整数。
我们考虑利用这个性质进行 dp,设 \(f(i,j)\) 表示当前枚举到第 \(i\) 位,\(i\) 后面的 \(b_i\) 已经填满了 \(1\sim j\),但是还没有填上 \(j+1\) 的方案数。同时为了计数方便,我们强制规定每一种数中的两个数是本质不同的,答案最后就需要除以 \(2^n\)。设 \(c_0\) 表示此时 \(i\) 后面钦定消失的柱子个数,\(c_1\) 为钦定不消失的柱子个数,分类讨论一下转移:
-
如果当前位置钦定消失:
显然此时 \(j\) 不会变,那么 \(1\sim j\) 总共有 \(2j\) 个可用的高度,前面填 \(1\sim j\) 这些柱子用了 \(j\) 个,同时填 \(c_0\) 还要用 \(c_0\) 个,所以转移为:
\[f(i,j)\leftarrow f(i+1,j)\times (j-c_0) \]注意这里转移讨论系数的方法比较重要,下文还会运用。
-
如果当前位置钦定不消失,那么还要分 \(j\) 是否改变讨论:
-
当 \(b_i > j+1\) 时:
显然此时 \(j\) 不会改变,至于当前的 \(a_i\) 取多少我们先不关心,把他的贡献延后计算,转移为:
\[f(i,j)\leftarrow f(i+1,j) \] -
当 \(b_i=j+1\) 时:
此时我们就需要把之前没有考虑到的 \(a_i\) 考虑一下了。枚举一下改变后的值 \(k\),先从后面的没有消失且没有填 \(1\sim j\) 的柱子中选出 \(b_i\) 为 \(j+2\sim k\) 的 \(k-j-1\) 个柱子,方案为 \(\binom{c_1-j}{k-j-1}\)。
然后再确定 \(a_i\) 的值,由于 \(b_i=j+1\),说明 \(a_i\) 只能在 \([j+1,k]\) 中取,套用第一类转移的套路,此时有 \(2(k-j)\) 个可用高度,已经用了 \(k-j-1\) 个,则剩下的有 \(k-j+1\) 种高度。
最后我们还需要知道后面的 \(k-j-1\) 个柱子填数的合法方案数,这个暂时还不知道,我们先记它为 \(g_{k-j-1}\)。那么转移方程就是:
\[f(i,k)\leftarrow f(i+1,j)\times \binom{c_1-j}{k-j-1}\times (k-j+1)\times g_{k-j-1} \]
-
现在考虑 \(g_n\) 怎么求,实际上 \(g_n\) 表示的就是 \(n\) 个柱子地震后恰好震成 \(1\sim n\) 的方案数,那么这个的转移和 \(f\) 的第二类转移实际上是比较类似的,我们枚举第一个柱子的最终高度为 \(x\),那么后面就应该分成两部分,一部分震成 \(1\sim x-1\),另一部分震成 \(x+1\sim n\)。先选出哪些柱子要震成 \(1\sim x-1\),方案数是 \(\binom{n-1}{x-1}\) 的,而这两部分选数的方案数则是 \(g_{x-1}g_{n-x}\)。
现在剩下的就是第一个珠子的初始高度的方案数,继续套用第一类转移的套路,当前初始值只能选 \([x,n]\),总共 \(2(n-x+1)\) 个可行高度,而后面占用了 \(n-x\) 个,所以方案数是 \(n-x+2\) 种。于是转移为:
那么我们就可以在 \(O(n^3)\) 的时间复杂度内完成这个 dp 了,可以通过。
G P9522 [JOISC 2022] 错误拼写
分析一下题目中给出的条件 \(T_{A_j}\le T_{B_j}\),我们先假设 \(A_j< B_j\),那么这个条件的含义就是在区间 \([A_j,B_j]\) 中,第一个不满足 \(s_i=s_{i+1}\) 的 \(i\) 满足 \(s_i>s_{i+1}\);否则的话就是反过来,表示在区间 \([B_j,A_j]\) 中第一个不满足的 \(i\) 满足 \(s_i<s_{i+1}\)。
我们把两种约束条件分开看,分别叫第一类约束和第二类约束。接下来直接 dp,设 \(dp(i,j)\) 表示枚举到第 \(i\) 位,当前字符为 \(j\) 的方案数。观察到上面的约束条件实际上只关注连续段的交界处的字符大小关系,所以转移的时候我们也只需要考虑连续段即可。接下来分类讨论:
-
当 \(s_i=s_{i+1}\) 时,显然这样填不会影响任何约束条件,直接转移即可:\(dp(i+1,j)\leftarrow dp(i,j)\)。
-
当 \(s_{i}> s_{i+1}\) 时,此时说明我们的一个连续段结束了,枚举上一个连续段的结尾 \(k\),则当前连续段就是 \([k+1,i]\)。由于 \(s_{i}>s_{i+1}\),所以说明如果一个约束条件 \([l,r]\) 如果满足 \(l\in [k+1,i],r\in [i+1,n]\),那么它必须是第一类约束。
换句话来讲,对于所有满足 \(r\ge i+1,l\le i\) 的第二类约束,\(k+1\) 都要小于 \(l\)。那么不难发现所有合法的 \(k\) 构成了一段区间,设其为 \([p,i-1]\),这个可以用
set
维护出来。然后考虑转移,由于 \(s_i>s_{i+1}\),所以枚举 \(s_{i+1}\) 为 \(x\),那么转移就是 \(dp(i+1,x)\leftarrow \sum dp(k+1,j)-dp(k,j)\)。由于 \(k\) 构成一段区间,所以中间的 dp 值应该全部消掉了,所以最后的转移就是 \(dp(i+1,x)\leftarrow dp(i,j)-dp(p,j)\)。再用后缀和优化一下就可以做到 \(O(|\Sigma|)\) 转移了。
-
当 \(s_i<s_{i+1}\) 时,与第二类情况类似,不再赘述。
于是我们可以在 \(O(n|\Sigma|+n\log n)\) 的复杂度内完成这个 dp,可以通过。
H P9312 [EGOI 2021] Lanterns / 灯笼
先考虑朴素的 dp 怎么做:枚举起点 \(i\),然后令 \(dp(l,r)\) 表示此时合法海拔范围在 \([l,r]\) 的最小花费。由于起点确定,海拔范围 \([l,r]\) 表示的区间就确定了。转移的时候枚举区间中的灯笼,能转移当且仅当这个灯笼的 \([A_i,B_i]\) 与 \([l,r]\) 有交集。复杂度是 \(O(n^3 k)\) 的。
考虑优化,首先要优化的就是将枚举起点这一步搞掉,方法很简单,转置一手即可。然后由于起点未知,\([l,r]\) 能表示的区间也就不知道了,所以还要记录区间中的一个点来转移。于是设 \(dp(l,r,i)\) 表示当前走到 \(i\),合法范围在 \([l,r]\),从这个状态开始走完还需要的最小花费。
这样做其实没有优化复杂度,不过这时候的瓶颈就在状态上了。考虑怎样优化状态,我们想办法把海拔上下界和区间位置这两个信息压缩起来,这时候不难想到,由于上下界是由区间中某两个点确定的,所以只需要知道这两个点的位置,就可以计算出区间位置和上下界了。所以设 \(dp(u,v)\) 表示当前海拔下界为 \(A_u\),海拔上界为 \(B_v\),区间经过 \(u,v\) 还需要的最小花费。
考虑转移,枚举区间中的灯笼,然后依然判断交集并转移即可,分类讨论一下有:
- \(f(u,v)=\min(f(u,v')+W_{v'})\),要求 \(B_v<B_{v'}\);
- \(f(u,v)=\min(f(u',v)+W_{u'})\),要求 \(A_u>A_{u'}\);
- \(f(u,v)=\min(f(p,p)+W_{p})\),要求 \(B_v<B_{p}\) 且 \(A_u>A_p\);
此时的复杂度是 \(O(k^2 n)\) 的,瓶颈在转移。考虑如何优化,先看第一种转移。由于整个过程实际上还是类似区间 dp,所以我们在枚举的时候 \(u\) 应该按 \(A\) 从小到大枚举,\(v\) 应该按 \(B\) 从大到小枚举。这时候会发现,对于 \(B_j>B_i\),如果 \(f(u,v')\) 无法转移到 \(f(u,j)\),那么它同样不可能转移到 \(f(u,i)\),原因显然。
于是我们就可以对 \(u\) 维护一个小根堆,存储所有还可以转移的 \(f(u,v')+W_{v'}\) 的值。当我们枚举到一个 \(f(u,v)\) 的时候,先弹出堆顶已经不能转移的 \(v'\),然后此时的堆顶就是一个合法转移点,直接转移即可。这样的话对于同一个 \(u\),每个 \(v\) 最多进堆一次,出堆一次,复杂度是 \(O(k\log k)\) 的,所以总复杂度就是 \(O(k^2\log k)\)。
对于第二种转移优化方式和第一种类似,不再赘述。现在看第三种转移,一个经典的思路是我们分步转移,也就是 \(f(p,p)\to f(p,v)/ f(u,p) \to f(u,v)\) 这样转移。这时会发现 \(f(p,v)\) 或者 \(f(u,p)\) 都是不合法的状态,不过问题不大,我们特判一下,把它们的值赋为 \(f(p,p)\) 即可。
如此我们的 dp 就优化到了 \(O(k^2 \log k)\),可以通过。
I P9528 [JOISC 2022] 蚂蚁与方糖
首先这道题就是让蚂蚁去找方糖,求最大匹配数。不难想到建立一个二分图最大匹配模型,左边是蚂蚁,右边是方糖。然后想到有关二分图匹配的 Hall 定理:对于二分图 \((A,B,E)\),其最大匹配数是 \(|A|-\max\limits_{S \subseteq A}(|S|-|N(S)|)\)。\(|A|\) 是好求的,就是所有蚂蚁的 \(A_i\) 之和。现在难点在于求后面这个式子。
首先,同一位置上的蚂蚁肯定尽可能全选,因为这样做邻域 \(N(S)\) 不会改变而 \(|S|\) 会增大。然后进一步发现,让后面的式子取到最大值的 \(S\) 是一段段区间,每两段区间的间隔至少是 \(2L\)。这个原因也比较显然,如果两个区间间隔 \(<2L\),那么把中间的蚂蚁全部加上不会改变邻域大小,而 \(S\) 的大小会增大。
所以我们可以形式化的写出后面式子的值,假设第 \(i\) 个区间包含第 \([l_i,r_i]\) 个蚂蚁,那么后面式子的值就是:
这个式子不好直接求最大值,我们希望把它拆成只和 \([l_i,r_i]\) 中每一项单独相关,这样的话用一些数据结构维护一下最大子段和就行了。考虑到后面的求和实际上就是所有蚂蚁的区间 \([X_i-L,X_i+L]\) 的并,而根据容斥原理,两个区间的并是两个区间的和减去两个区间的交,而交也是好求的,所以上式可以改写为:
此时对于每个蚂蚁出现的位置,我们只需要维护出该位置上有多少蚂蚁、这个位置对应的区间内有多少方糖,以及这个位置和下一个位置交的地方有多少方糖即可。现在考虑怎样维护最大的区间和,这个显然可以上线段树,在每一个节点设 \(f(i,0/1,0/1)\) 表示当前节点区间左 / 右端点钦定选 / 不选的方案数。转移的时候枚举 \(l,mid,mid+1,r\) 处是否钦定选然后合并即可;当 \(mid,mid+1\) 处都钦定选的时候要加上区间交的方糖个数,在每个线段树节点上再维护一下 \(mid,mid+1\) 的区间交的方糖个数即可。
然后考虑如何修改,蚂蚁的修改是简单的,直接找到对应位置单点加即可。方糖的修改比较麻烦,考虑一块方糖会影响 \([X_i-L,X_i+L]\) 区间内的所有蚂蚁,那么在线段树上找到对应区间之后,只要这个区间内选了蚂蚁,邻域就会加上这些方糖的大小,也就是 dp 值要对应减去(注意此时 \(dp(i,0,0)\) 不一定选了蚂蚁,所以需要和 \(0\) 取 \(\max\))。然后还需要同时维护区间中点处区间交的方糖个数,这个比较简单,判断一下中点和 \([X_i-L,X_i+L]\) 的位置关系然后直接修改即可。
最后式子的最大值就是线段树根节点处所有 dp 值的最大值,然后用 \(\sum A_i\) 一减就是最终答案。复杂度是 \(O(Q\log Q)\) 的,不过合并的时候会带一个 \(16\) 的常数。
J P5281 [ZJOI2019] Minimax 搜索
首先我们先跑一遍正常的搜索,把每个点的初始权值 \(col_i\) 求出来。令 \(W=col_1\),那么 \(W\) 节点的根链上的所有节点的权值都应该是 \(W\),而只要这里面有一个点的权值改变了,根节点就改变了。所以实际上我们可以把这条链上的边断开,对于每个连通块计算其对应方案数,然后组合起来即可。
现在考虑计数,首先直接算 \(w(S)=k\) 的方案数比较困难,一个经典的想法是先求 \(w(S)\le k\) 的方案数然后再做差分。然后考虑树形 dp,设 \(dp(i)\) 表示 \(i\) 子树内的所有叶子构成的集合的非空子集中,有多少集合 \(S\) 可以使得在 \(k\) 次操作以内让 \(col_i<W\)。由于把叶子改成 \(W\) 没有意义,所以我们不用管 \(col_i=W\) 的情况,那么 \(col_i>W\) 的集合个数就是总方案数 \(cnt_i\) 减去 \(dp(i)\) 了。此时答案就可以写成:
然后考虑转移,这个也不是很困难,算方案的时候用一下容斥即可:
最后我们来考虑一下初值。首先我们需要明白一件事:对于同一个连通块内的叶子,它们要改大还是改小取决于根节点在原树上的深度。如果根节点原本深度是奇数,那么所有点一定都是往大改的,否则的话就是往小改,这样的话才是最优的。那么根据这个,可以得出:
- 当根节点深度为奇数时,叶子节点 \(x\) 的初值为 \(dp(x)=[x<W]+[x+k<W]\)。
- 当根节点深度为偶数时,叶子节点 \(x\) 的初值为 \(dp(x)=[x<W]+[x-k<W]\)。
我们从小往大枚举 \(k\),发现此时 \(k\) 增加时每次只会有最多 \(1\) 个位置的 dp 值发生改变。所以我们可以尝试使用动态 dp 维护上述过程。记:
那么上述转移可以写作:
同时有:
那么对原树做重链剖分,转移可以写成(其中 \(\text{son}(i)\) 为 \(i\) 的重儿子):
把上述转移写成矩阵形式为:
用线段树维护一下左侧矩阵即可,总复杂度是 \(O(n\log^2 n)\) 的。还需要注意的一点是我们处理轻儿子 dp 值积的时候会出现乘以 \(0\) 或除以 \(0\) 的情况,这个时候需要维护一下儿子中 \(0\) 的数量来特判。
K P8334 [ZJOI2022] 深搜
首先最小值期望不好直接转移,所以考虑拆成最小值乘上概率。如果用传统的树形 dp 状态数必然是两维的,承受不了,考虑另一种思路。我们从大往小枚举当前最小值 \(k\),只保留所有权值 \(\ge k\) 的节点,然后直接算可达概率和即可。这样的话我们求出来的就是最小值 \(\ge k\) 的概率总和,在最后做一下差分即可得到真实答案。
接下来令权值 \(\ge k\) 的节点为白点,权值 \(<k\) 的为黑点,记作 \(col_i=1/0\);子树内全为白点的子树为白子树,否则为黑子树,记作 \(tag_i=1/0\)。设 \(f(i)\) 表示从 \(i\) 出发,只经过白点到达子树中每个点的概率总和。
考虑转移,先判掉 \(col_i=0\) 的情况,然后枚举转移的子树;但是此时我们并不一定是直接走进这个子树,而是有可能先走一些白子树后再走。设 \(cnt_i\) 表示 \(i\) 有多少个黑子树,那么这个概率就是 \(\tfrac{1}{1+cnt_i-(1-tag_j)}=\tfrac{1}{cnt_i+tag_j}\),原因在于我们随机到白子树相当于重选,而第一次在其他黑子树与目标子树之间选择的概率就是上式。于是不难得出转移方程:
然后我们再设 \(g(i)\) 表示子树内所有 \(f(i)\) 的和,答案就是 \(g(rt)\)。这样的话我们就可以得出一个 \(O(n^2)\) 的做法。考虑优化,我们在 \(k\) 减小的时候,改变的值有 \(col,cnt,tag\),而这些值的改变总次数都是 \(O(n)\) 的,所以考虑使用动态 dp。
先把 \(f(i)\) 拆开:
显然这可以写成 \(f(i)=af(\text{son(i)})+b\) 的形式,然后用矩阵优化。不过这里会发现,当我们修改 \(cnt_i\) 的时候,\(b\) 的变化是不好计算的,原因在于我们没有把轻子树贡献单独拎出来。这里运用一个经典技巧,我们统计轻子树贡献的时候开两个 \(f\),分别统计 \(tag=0\) 和 \(tag=1\) 的贡献和 \(fa,fb\),这样前面的和式就可以直接单独计算了。
最后我们再维护一下轻儿子 \(g(i)\) 的和 \(g\),那么我们就可以写出转移矩阵:
这样的话用全局平衡二叉树维护一下就可以做到 \(O(n\log n)\) 了。注意我们在全局平衡二叉树上找 \(tag\) 的时候找的不是当前树根,而是对应链的链头。