动态规划做题记录-2
动态规划做题记录-2
AGC028D. Chords
考虑断环为链,那么两个线段相交当且仅当他们仅相交不包含。我们用 \((l,r)\) 表示一个连通块当且仅当 \(l,r\) 是一个连通块的两个端点,也就是最小点和最大点。那么我们考虑枚举每一个连通块 \((l,r)\),考虑它们对答案的贡献,也就是这个连通块在多少种方案中存在,令 \(f_{l,r}\) 表示这个值。
注意到,如果 \((l,r)\) 是一个连通块,那么 \(r-l+1\) 应为偶数,并且其中的所有点不能向外有连边,需要先特判一下。否则我们考虑在整个区间内随意连边的方案数 \(g_{c(l,r)}\),其中 \(c(l,r)\) 是 \([l,r]\) 中没有连边的点的个数,\(g_x\) 当 \(x\) 为奇数时等于 \(0\),否则等于 \(\prod_{k=1}^{\frac{x}{2}}(2k-1)\)。然而我们随意连边 \([l,r]\) 不一定成为连通块,这时候可以进行容斥,将 \(l\) 和其他点形成连通块的方案数减去,那么我们有转移方程:
可以在 \(O(n^3)\) 的复杂度内完成转移,统计答案即为 \(\sum f_{l,r}g_{2n-2k-c(l,r)}\)。
AGC022F. Checkers
考虑让 \(A\) 关于 \(B\) 对称变成一条 \(A\to B\) 的边,那么因为进行了 \(n-1\) 次操作,于是我们得到了一棵树。考虑一次对称后的坐标为 \(2x_{B}-x_A\),最多 \(n-1\) 次对称支持某一个坐标前的系数达到 \(2^{n-1}\),远不及 \(10^{100}\),因此最终的坐标是否相同仅与每个位置前的系数相关。
考虑对于一棵树而言,我们能够轻易得出每个位置前的系数的绝对值为 \(2^{\text{dep}}\),而较为困难的是正负号,从而我们可以考虑以这个内容入手解决。对于正负号而言,其不仅与儿子节点的个数有关,还与对称的顺序有关,然而在父亲节点往上其和父亲接受的取反是同时的,于是我们考虑维护每个位置和其父亲对答案贡献的正负性是否相同。
发现对于一个有 \(\text{son}\) 个儿子的节点,共有 \(\lfloor\frac{\text{son}}{2}\rfloor\) 个点与其取反相同(忽略儿子的儿子对儿子的影响)。于是我们设 \(f_{i,j}\) 表示选择了 \(i\) 个点且最后一层有 \(j\) 个有奇数个儿子的点。接着我们枚举这一层节点的个数 \(k\),那么因为每一个奇数节点的点会损失一个儿子使得取反相同,那么总共和父亲取反相同的点的数量 \(t\) 为 \(\frac{k-j}{2}\),如果 \(k-j\) 为奇数,则肯定不合法,不予考虑。接着我们关注这一层有多少个节点有奇数个儿子。考虑枚举这一层有多少个节点和父亲对答案位置的贡献的正负号是相同的,假设为 \(p\),那么说明这一层至少有 \(|t-p|\) 个奇数个儿子的节点。而更多的节点是没有意义的,因为我们总是选取多余的偶数个奇节点合并到剩下的奇节点上使得对答案的贡献不变,那么我们只需要考虑对 \(|t-p|\) 的转移即可。于是我们又转移方程
于是我们可以做到 \(O(n^4)\) 的复杂度。
UOJ607.【UR #20】跳蚤电话
考虑什么样的方案才会得到最后的配对方案。我们发现第二种操作在树上相当于覆盖了一条简单路径,因为我们肯定可以通过第一种操作不断补齐这条链路径在树上的每一条边,具体操作就是选择一条边 \((u,v)\),在 \(u,v\) 之间的简单路径上选择一个点 \(w\),对这三个点操作,可以将连边改为 \((u,w)(w,v)\),这样不断细化一定可以配对路径上的所有边。也就是说,如果存在恰好一条路径覆盖了这条边,那么这条边一定会在最后刚好有一个配对。然而如果存在多条路径覆盖这条边,说明这条边被多次配对,这显然不合法。通过进一步分析我们发现,在树上连边的任意时刻,这个树总是不存在横叉边。否则考虑边 \((u,v)\) 是横叉边,因为 \((u,v)\) 能够存在,说明两者其中一个点到根的链已经被覆盖,如果再将 \(u,v\) 之间的路径覆盖,那么显然有一条边会被覆盖两次,这显然不合理。
通过上面的过程,我们将操作改写,初始时 \(1\) 既被覆盖也被选择:
- 选择一个已经被覆盖但未被选择的点,将其的状态设为被选择。这相当于在进行原来的第二种操作。
- 选择一个被选择的点,然后选择一个子树内的点,使得两个点之间的简单路径上没有任何点被覆盖(除子树的根节点),将这条路径上的点的所有状态设为被覆盖,将选择的点的状态设为被选择。这相当于在进行原来的第一种操作。
那么题目本质上想要询问将所有点全部选择的方案数。继续分析我们发现,对于所有点来说,在每一种情况下,只可能有一种将状态设为被选择的方案(如果未被覆盖则只能通过改写的第二个操作,否则只能通过改写的第一个操作),那么我们可以只关心每个点被选择的顺序序列 \(p\),那么我们仅需得到合法的 \(p\) 的数量。
继续思考,我们发现在一个子树的根节点被选择前,这个子树内一定最多只有一个子树的点被选择。我们从这一点入手开始 dp:设 \(f_{u}\) 表示在 \(u\) 的子树中将所有点均选择的方案数,\(\text{siz}_u\) 为 \(u\) 的子树的大小。那么会有以下两种可能。
-
先选择了根节点。此时这个子树的 \(p_1\) 一定是 \(u\),操作所有剩下所有子树的顺序互不干扰,就是可以在保证每个子树原来 \(p\) 顺序不变的基础随意排列。那么总方案数为
\[{\text{siz}_u\choose\text{siz}_{\text{son}_1},\text{siz}_{\text{son}_2},\cdots,\text{siz}_{\text{son}_k}}\prod f_v \] -
先选择了某个子树,假设为 \(\text{son}_i\),那个在下一个子树的点出现之前一定会加入 \(u\),因此剩下的子树的操作方案数为上面的式子去掉 \(\text{son}_i\) 的贡献。然后考虑操作 \(\text{son}_i\) 和其他子树的顺序,显然在操作完 \(\text{son}_{i}\) 的第一个点后,剩下的点可以在顺序不变的情况下乱排,那么总方案数就是
\[\begin{aligned}&{\text{siz}_u-\text{siz}_{\text{son}_i}\choose \text{siz}_{\text{son}_i}-1}f_{\text{son}_i}\times{\text{siz}_u-\text{siz}_{\text{son}_i}\choose\text{siz}_{\text{son}_1},\cdots,\text{siz}_{\text{son}_{i-1}},\text{siz}_{\text{son}_{i+1}},\cdots,\text{siz}_{\text{son}_k}}\prod_{v\ne\text{son}_i} f_v\\=&\frac{\text{siz}_{\text{son}_i}}{\text{siz}_u-\text{siz}_{\text{son}_i}}{\text{siz}_u\choose\text{siz}_{\text{son}_1},\text{siz}_{\text{son}_2},\cdots,\text{siz}_{\text{son}_k}}\prod f_v\end{aligned} \]
那么可以直接枚举所有儿子算出贡献相加得到 \(f_u\),注意 \(f_1\) 只有第一种贡献,复杂度是 \(O(n)\) 的。
UOJ181.【UR #12】密码锁
考虑到竞赛图缩点之后一定是一条链,考虑到这条链上的前缀的点集一定没有入边,而链上强连通分量的个数和前缀的个数一样,我们从这一点入手可以给出一个 \(O(n^22^n)\) 的算法,只需要暴力枚举所有点集然后接着暴力枚举所有边统计这个点集是一个前缀的概率。由于期望的线性性,只需要将所有点集的概率简单相加即为答案。
考虑这个算法的优化,我们发现除了 \(m\) 条边以外的所有边的边权均为 \(\frac{1}{2}\),也就是说假如这个点集大小为 \(i\) 且向外没有特殊边,那么贡献一定是 \((\frac{1}{2})^{i(n-i)}\),否则一条概率为 \(p\) 的特殊边的贡献是 \(2p\),我们只需要对于每一个子集枚举所有特殊边判断其是否对概率有贡献即可,复杂度优化到 \(O(m2^n)\)。
考虑相对而言 \(m\) 较小,我们尝试将 \(m\) 挪到指数的位置上。因为特殊边只会对包含其中某些点的点集作贡献,我们不妨考虑枚举所有特殊边的连通块,这样我们只需要知道点集包含其中的哪些点即可做出贡献。我们枚举这个弱连通块内的点是否选择的状态,然后枚举所有特殊边统计贡献。因为这个连通块之前没有选择过,因此只要选择当前的状态,就一定会多选择 __builtin_popcount
个点,那么我们可以进行一个背包 dp,统计出所有大小为 \(i\) 的点集的贡献和,最后统一贡献给答案。因为 \(m\) 条边的连通块最多有 \(m+1\) 个点,所以这样复杂度是 \(O(n^22^m)\) 的。
但是你可以考虑在对于连通块进行背包 dp 的过程中,我们可以将贡献统一延后计算,也就是在枚举完所有状态后,对所有新增个数的贡献背包,那么复杂度可以降到 \(O(n2^m)\)。
UOJ370.【UR #17】滑稽树上滑稽果
考虑有根树纯粹在扯淡,因为你构造出一条链肯定更优,于是我们考虑如何构造一条最优的链。考虑所有 \(a_i\) 的 \(\text{and}\) 和 \(A\),我们肯定可以通过最多 \(\log w\) 个点使得 \(\text{and}\) 和达到 \(A\),所以我们不妨考虑在什么时候前缀的 \(\text{and}\) 和为 \(A\)。设 \(f_{i}\) 表示前 \(i\) 个位置的元素的 \(\text{and}\) 和为 \(A\) 的最小代价,那么答案即为 \(\min\{f_i+(n-i)A\}\)。具体维护可以考虑转移 \(g_{i,S}\) 表示前 \(i\) 个元素的 \(\text{and}\) 和为 \(S\) 的最小代价,这个可以轻松做到 \(O(nV\log V)\)。有一个经典的内容是我们可以将代价放到 dp 里面计算,就是预先提供后面的代价,有新的代价时将后面的代价替换,这样可以做到 \(O(nV)\)。
我们考虑不要枚举每一个元素去转移,而是考虑每一个状态之间的转移是否可行。更具体的,考虑到 \(S\) 总是转移到自己的子集,我们考虑枚举所有 \(S\) 的子集 \(T\),那么能够转移到 \(T\) 的必要条件是存在一个 \(S\oplus T\oplus U\) 的子集的元素(\(U\) 是全集),这一点可以通过高维前缀和做到 \(O(V\log V)\)。但是我们其实不关心是否能恰好转移到正确的位置,因为我们转移到一个错误的位置一定不优,而正确的位置也一定会被转移到,所以正确性是没有问题的,复杂度是 \(O(3^{\log V})\)。
UOJ312.【UNR #2】梦中的题面
首先考虑对于 \(m\) 个数,每个数在 \([0,n]\) 之间,要求和 \(\le n\) 的方案数。这个不难通过组合数算出是 \({n+m\choose m}\) 的。接着考虑对于这个问题特殊的上界要求,我们选择进行容斥,即钦定一定超过上界的数的集合 \(S\),通过二项式反演可以得到答案即为
我们通过这一点进行 dp,通过 Vandermode 卷积我们有
于是我们可以拆分贡献,将大的组合数拆分到每一个被选择在 \(S\) 的集合中的元素去统计答案,即令 \(f_{i,k}\) 表示前 \(i\) 个元素在组合数底数为 \(k\) 意义下的答案和,答案即为 \(f_{m,m}\)。但是考虑我们如果需要利用卷积去统计答案,则需要用到实数域的组合数,即顶数为负数的组合数,但是在原式中,\(n-\sum_{i\in S}b^i+(c-1)|S|<0\) 时这个式子没有贡献,然而在实数域里,我们会统计它的贡献。因此我们需要保证选择的 \(S\) 满足 \(\sum_{i\in S}b^i-(c-1)|S|\le n\)。考虑用数位 dp 去限制这个内容,即考虑从高到低将能否选择某个数超过限制变成一个二进制数,并要求字典序最大。这样之后一个合法的二进制状态一定满足其不超过这个上限状态,因为后面所有数的和都不会比当前这个大。那么我们新开一位表示当前的选择是否顶满上界,然后从大到小枚举每一位转移即可,复杂度是 \(O(m^3)\) 的。至于快速统计上限状态,我们可以将 \(n\) 转成 \(b\) 进制数,然后判断减去某个上界后是否变为负数即可。
CF301E. Yaroslav and Arrangements
因为两个重排的序列不能由一个原序列重排得到当且仅当存在一个数字,两者的出现次数不一样,因此我们只关心每种数字的个数。考虑往序列里从小到大插数,我们只关心最小值为 \(1\) 的情况,因为其他情况可以平移得到。考虑如果当前方案有 \(p\) 对相邻的 \(1\),那么为了让序列合法,我们的 \(2\) 至少要有 \(p\) 个,假如我们插入了 \(c\) 个 \(2\),那么可以简单得出有 \(c-p\) 个相邻的 \(2\),并且有 \({c-1\choose p-1}\) 种方案得到这个结果。于是我们可以设 \(f_{i,s,p,l}\) 表示前 \(i\) 个数共选择了 \(s\) 个,有相邻的 \(p\) 个 \(i\),且重排数为 \(l\) 的序列个数。那么转移是容易的:
于是我们可以做到 \(O(n^5)\) 的转移,因为常数很小所以可以通过。
CF739E. Gosha is hunting
首先能想到一个容易的 \(O(n^3)\) dp。考虑使用 wqs 二分,对于每一个超级球额外有 \(k\) 的代价,这样可以去掉最后一维,复杂度降到 \(O(n^2\log n)\)。
接下来的做法是没有正确性的,我们考虑再次 wqs 二分,因为这个凸包是二维的,那么我们可以再次去掉一维,复杂度降到 \(O(n\log^2 n)\)。这个做法之所以失败是因为在离散的角度上你二维凸包切到的是一个面,所以你做不到完美选取到你要的点。但是你完全可以忽略这个事情,因为你二分最终的精度可以足够小,小到支持你通过本题。
LG9479. [NOI2023] 桂花树
考虑从部分分入手,关注 \(m=2\) 的情况,观察样例我们发现 \(k=0\) 和 \(k=1\) 的总方案数是不同的。我们考虑从小到大插入新增的点,如果从小到大插,那么不会有问题,两次的总方案数相乘即可,是 \((2n-1)(2n+1)\)。考虑 \(k=1\) 时我们可以将 \(n+2\) 单独挂在 \(n+1\) 下面,这是 \(k=0\) 所不行的,因此方案数为 \((2n-1)(2n+1)+n-1\)。
结合的过程,我们可以看出题目本质上要求一棵树,使得 \([1,i]\) 中的结点所产生的虚树的编号在 \([1,i+k]\) 中。考虑维护这个虚树假设树的大小为 \(c\),每一次我们可以直接将这个节点插入到一条边或者一个点下面,方案数为 \(2c-1\);或者可以将某一条边分叉,使得多出一个需要后面填入的点,方案数 \(c-1\);或者用这个点补全之前的一个未填入的点。考虑现在如果插入了 \(i\) 号结点,那么 \(i-k\) 号所产生的未填入的一定需要被填入,那么我们就只关心 \([i-k+1,i]\) 号点所产生的未填入的点是否填入,用状压维护这个内容。令 \(f_S\) 表示 \([i-k+1,i]\) 号点的未填入点的填入状态未 \(S\),那么每次可以
- 填入一条边或一个点下方,\(f_{S\setminus \{i-k\}}\xleftarrow{+}(2c-1)f_{S}\)。
- 分叉一条边产生新的结点,\(f_{S\cup\{i\}\setminus \{i-k\}}\xleftarrow{+}(c-1)f_{S}\)。
- 填入某个未填入的结点,\(f_{S\setminus\{i-k,j\in S\}}\xleftarrow{+}f_{S}\)。
于是我们可以做到 \(O(mk2^k)\) 转移,瓶颈在最后一个转移,需要卡常。
JOISC2017G. 長距離バス
考虑因为司机一定不能下车,所以每一次的下车狂潮肯定是乘客按 \(D\) 排序后某个 \([1,i]\) 的后缀,并且不难发现这个 \(i\) 是到达当前服务站的最后一个需要喝水的乘客。考虑如果某一个乘客要下车,那么肯定越早越好,那么我们只关心每个乘客作为最后一个喝水的乘客最早在什么时候,之前喝了几次水,记为 \(t_i\)。那么我们令 \(f_i\) 表示前 \(i\) 个乘客的最小代价。有以下两种转移:
-
这个乘客不下车,那么会有 \((\lfloor\frac{X}{T}\rfloor+[D_i\le X\bmod T])W\) 的代价。
-
这个乘客下车,那么为了转移的合理,这个乘客一定是一个服务站前的最后一个乘客,我们枚举在这个服务站到底下了哪些乘客,那么有转移
\[f_i=\min_{j=0}^{i-1}f_j+(C_i-C_j)+(i-j)t_iW \]这里的 \(C\) 是原来 \(C\) 的前缀和,不难看出可以斜率优化 dp。
综上,复杂度可以做到 \(O(n\log n)\)。
LG5405. [CTS2019] 氪金手游
考虑题目给出的特殊条件就是如果将 \((u,v)\) 视作无向边,所有边形成一棵树。考虑有向边时答案的统计,如果这棵树是外向树的话,那么我们可以简单利用 dp 统计答案。令 \(f_{i,s}\) 表示 \(i\) 的子树内的抽卡顺序符合要求,且所有卡的 \(w\) 的和为 \(s\) 的概率。首先根节点肯定要第一个被抽到,那么这个事件的概率是 \(\frac{w_i}{s}\),接下来因为子树互不干扰,所以我们可以分开当成子问题来考虑,注意到如果抽到已经出现过的内容相当于重抽,因此对于子树内的概率是没有影响的,所以可以做一个树上背包来解决,复杂度是 \(O(n^2)\) 的。
现在考虑内向边对答案的影响,我们考虑直接容斥,即钦定 \(i\) 条内向边一定违反限制,剩下的内向边随意且符合要求的的概率 \(g_i\),那么原答案即为 \(\sum_{i}(-1)^ig_i\),那么我们可以在转移中实现这个内容,即将 \(-1\) 作为系数直接转移,那么复杂度不变是 \(O(n^2)\) 的。
CF1152F2. Neko Rules the Catniverse (Large Version)
考虑按照位置 dp 较为困难,不妨按值域 dp,那么就是考虑如何插入 \(i\)。考虑之前的序列内容,我们只能讲 \(i\) 插在开头或者某个 \(\ge i-m\) 的数的后面,因为 \(m\) 只有 \(4\),所以我们可以用状压维护,同时我们发现我们还需要知道序列目前的长度,于是可以设 \(f_{i,l,S}\) 为插入 \(i\) 后序列总长为 \(l\),\([i-m+1,i]\) 中的数在序列中的情况为 \(S\) 的方案数,那么转移是容易的:
- \(i\) 不插入序列,转移为 \(f_{i,l,S\setminus\{i-m\}}\xleftarrow{+}f_{i-1,l,S}\)。
- \(i\) 插入序列,转移为 \(f_{i,l+1,S\setminus\{i-m\}\cup\{i\}}\xleftarrow{+}(\text{popcount}(S)+1)f_{i-1,l,S}\)。
那么复杂度是 \(O(nk2^m)\) 的,可以通过弱化版。考虑每一次的转移不随 \(i\) 改变,因此考虑用矩阵快速幂优化 dp,则复杂度为 \(O((k2^m)^3\log n)\)。
CF1608F. MEX counting
考虑我们前面放置的数会对后面产生贡献,但是我们可以用贡献延后计算的技巧忽略掉这一点,也就是我们不关心到底放了什么数字,因为我们可以在后面自己钦定。因此我们设 \(f_{i,j,k}\) 表示前 \(i\) 个数的 \(\text{MEX}\) 为 \(j\),并且 \(>j\) 的不同的数有 \(k\) 个的方案数。那么有以下转移:
- 这一位的数字 \(<j\),那么不会对 \(\text{MEX}\) 产生影响,并且也不会影响 \(k\),因此有 \(f_{i+1,j,k}\xleftarrow{+}jf_{i,j,k}\)。
- 这一位的数字 \(=j\),那么会对 \(\text{MEX}\) 产生影响,我们考虑枚举 \(\text{MEX}\) 具体变大了多少,这样我们可以知道前面一定有对应的数字出现,钦定其中的一些即可,也就是 \(f_{i+1,j+m+1,k-m}\xleftarrow{+}\frac{k!}{(k-m)!}f_{i,j,k}\)。
- 这一位的数字 \(>j\) 且在之前的 \(k\) 种数中,不会影响 \(\text{MEX}\) 和 \(k\),转移是 \(f_{i+1,j,k}\xleftarrow{+}kf_{i,j,k}\)。
- 这一位的数字 \(>j\) 且不在之前的 \(k\) 种数中,但我们实则不关心这个数具体是多少,因为后面转移是一定会钦定这个数的大小,因此直接转移 \(f_{i+1,j,k+1}\xleftarrow{+}f_{i,j,k}\) 即可。
答案的统计就是将未钦定完的数钦定即可,因为关注到 \(\text{MEX}\) 这一维有效的内容只有 \(O(k)\) 个,所以复杂度是 \(O(n^2k^2)\) 的。考虑如何优化,复杂度瓶颈显然在第二种转移,转成填表就是 \(f_{i,j,k}=\sum_{m\ge 0}\frac{(k+m)!}{k!}f_{i-1,j-m-1,k+m}\)。我们发现分母处是和转移点相同的内容,可以在转移的时候直接乘,而剩下的部分满足后两维的和是 \(j+k-1\),因此我们维护一个前缀和数组 \(w_{x,y}=\sum_{m=0}^{y}f_{i-1,j,x-j}(x-j)!\) 的值即可做到 \(O(n^2k)\)。
这么写常数会比较大,我们关注到 \(f_{i,j,k}\) 的转移内容和 \(f_{i,j-1,k+1}\) 的转移内容仅仅相差 \(f_{i-1,j-1,k}\) 这一项和分母阶乘,然而这两种都可以 \(O(1)\) 直接统计贡献,那么可以直接通过前面的转移推出后面的内容。
CF708E. Student's Camp
首先容易直接计算出一边被破坏了 \(x\) 个格子的概率 \(P(x)={k\choose x}p^x(1-p)^{k-x}\),因为两边互不干扰,所以某一行被破坏到 \([l,r]\) 的概率是容易算出为 \(P(l-1)P(m-r)\)。一个 naive 的想法是直接 dp,令 \(f_{i,l,r}\) 表示第 \(i\) 行被破坏到仅剩 \([l,r]\) 时前 \(i\) 行连通的概率,转移只需要找到所有 \(l'\le r,r'\ge l\) 的 \(f_{i-1,l',r'}\) 的和即可,利用二维前缀和可以做到 \(O(nm^2)\)。
我们考虑优化,更具体的,我们考虑不计算所有符合转移条件的位置的概率和,而是计算所有位置的概率和减去不符合转移条件的位置的概率和,即
于是我们可以考虑迅速维护中间的三个和式,分别记为 \(F(i-1),L(i-1,l),R(i-1,r)\)。我们关注到,\(F(i)\) 即为前 \(i\) 行连通的概率,因此只需要求出 \(F(n)\) 即可,并且 \(L,R\) 的形式高度对称,也就是说我们有 \(L(i,x)=R(i,m+1-x)\),所以我们下文只关注 \(L\) 的求法。
可以考虑维护一个 \(S(i,x)\) 表示所有右端点为 \(x\) 的位置的概率和,那么我们有 \(L(i,l)=\sum_{x<l}S(i,x)\),并且,我们发现 \(F(i)\) 就是所有 \(S\) 的和,于是我们只需要能够快速求出 \(S\) 就可以推出所有内容,考虑直接将 \(f\) 的转移式代入,我们有:
于是我们可以在 \(O(nm)\) 的复杂度内求得所有内容。
LG3685. [CERC2016] 不可见的整数 Invisible Integers
考虑到题目中给出的限制非常的恶心,直接 dp 的限制会特别多,因此我们很难设计一个状态使得我们可以容易的转移。但是关注到这题的 \(n\le 10\),并且长度最多也只有 \(9\),那么我们可以考虑一个近似搜索的思路。
我们从左往右填数,考虑只有向右的提示时我们如何 dp。容易想到我们进行状压,并考虑当前正在完成哪一个提示,完成到了第几位。令 \(f(S,i,l,j,r)\) 表示完成的提示状态为 \(S\),当前正在完成第 \(i\) 个和第 \(j\) 个提示,第 \(i\) 个提示向右并且现在需要填写第 \(l\) 位,第 \(j\) 个提示向左并且现在需要填写第 \(r\) 位,并且最终完成所有提示的最小所需长度。注意这里即使是向左的提示我们依然从左向右填写,因此向左的匹配的填写是倒序的,特别的,当 \(l=\text{len}(i)+1\) 时,说明 \(i\) 已填写完毕,\(r=0\) 时,说明 \(j\) 这个提示已经完成。那么我们有以下转移:
-
直接填写 \(i\) 接下来所需要的数字。此时的所需长度为 \(f(S,i,l+1,j,r)+1\),并且需要保证填写不会导致 \(j\) 的失败,也就是当前填写的数字 \(x\) 在 \(j\) 中的出现位置不能在 \(r\)(包括 \(r\))之前,或根本没有出现,否则在 \(j\) 中的 \(x\) 会提前出现。
-
直接填写 \(j\) 接下来所需要的数字。此时的所需长度为 \(f(S,i,l,j,r-1)+1\),同理填写的 \(x\) 在 \(i\) 中不能晚于 \(l\)(包括 \(l\))出现,或根本没有出现,否则在 \(i\) 中 \(x\) 会提前出现。
-
直接填写 \(i,j\) 接下来一个共同需要的数字。此时的所需长度为 \(f(S,i,l+1,j,r-1)+1\)。
-
将 \(i\) 替换成其他还未考虑的提示。此时的所需长度为 \(f(S\cup\{i\},p ,1,j,r)\),并且需要保证完成 \(p\) 这个提示不会导致 \(i\) 这个提示的失败,也就是当你成功完成 \(p\) 这个提示的时候,你可以保证 \(i\) 也一定完成了。
我们可以预处理这个内容,因为 \(p\) 总是从头开始,所以我们只关心 \(i,l,p\) 三个参数。设 \(g(i,l,p)\) 表示当 \(i\) 需要填写第 \(l\) 位的时候,能否在后面接入 \(p\)。如何求解是容易的,我们暴力跑一遍 \(p\) 的内容,并动态维护当前的 \(i\) 匹配到的位置 \(l'\)。如果当前这一位的 \(x\) 可以匹配,那么对 \(l'\) 加 \(1\),否则判定 \(x\) 在 \(i\) 中的位置是否在 \(l'\) 前,显然如果这个位置不在 \(l'\) 之前,或根本没有出现,那么 \(x\) 在 \(i\) 中会提前出现。到最后,如果 \(i\) 全部匹配,那么可以认为 \(g(i,l,p)\) 为真。
-
如果 \(j\) 已经完成,找一个还未考虑的提示 \(p\),并枚举其当前应该填写的位置 \(r'\)。此时的所需长度为 \(f(S\cup\{j\},i,l,p,r')\),并且需要保证完成 \(j\) 的时候,我们可以认为 \(p\) 已经该填写 \(r'\)。
先解释为什么不像上一个转移一样动态转移,这是因为你的上一个提示可以对你的这一个提示做贡献,因此你是不知道这一个提示的哪些位已经被匹配了,如果你在所有位置暴力去找这个长度,因为每一个位置其实只对应一个最优长度,所以你不如在完成这个提示时重新枚举。考虑怎样判定 \(p\) 是否可以已经填写 \(r'\) ,我们将整个序列颠倒过来看,我们发现 \(p,r',j\) 之间满足的内容其实和之前我们维护的 \(i,l,p\) 是相同的,直接用即可。
可以用记忆化优化整个过程。关于复杂度,我们发现有 \(O(2^n(10n)^2)\) 个状态,其中有 \(O(2^n10n^2)\) 个状态的转移是 \(O(10n)\) 的,\(O(2^n(10^n)^2)\) 个状态的转移是 \(O(n)\) 的,因此总复杂度是 \(O(2^n(10n)^2n)\)。具体实现的细节是很多的,因为在开始某一种提示的填写时,另一种可能还没有需要填写的提示,因此我们可以新增一个字符串 \(n+1\) 当作还没有开始的标志,而用字符串 \(0\) 表示已经结束。需要注意因为向右的提示直到最后都会被影响,因此 \(i\) 始终不为 \(0\)。
GYM103447A. So Many Lucky Strings
因为回文串满足在一个回文串两边加入相反的串得到的是回文串,这会启发我们进行区间 dp。令 \(f(l,r,d)\) 表示匹配回文串的最左和最右的字符串分别为 \(l,r\),并且 \(l\) 中有长为 \(d\) 的前缀没有成功匹配上回文串的方案数,同样我们还需要一个 \(g(l,r,d)\) 表示 \(r\) 中有长为 \(d\) 的后缀没有成功匹配上回文串的方案数。这个设计的状态数是 \(O(n^2\sum|s|)\) 的,但是仔细想想,我们发现不是每一维都需要开到 \(O(\sum|s|)\),因为如果这个长度大于最外层的字符串,那么这个过程是无用的,因此总状态数可以用 vector
优化到 \(O(n\sum|s|)\)。
现在考虑转移,\(g\) 和 \(f\) 有相似的转移,因此这里只陈述 \(f\) 的转移。考虑最外层的两个字符串是匹配的,那么上一次操作一定是添加了这两个中的某一个字符串,我们对此进行分类讨论:
- \(\text{len}(l)-d\le\text{len}(r)\),这个时候说明上一次添加的是 \(l\),把 \(r\) 所欠的长为 \(\text{len}(l)-d\) 的后缀匹配了。因此我们先考虑两者是否能够匹配,如果可以,则应该加上 \(\sum_{l'>l}g(l',r,\text{len}(l)-d)\)。
- \(\text{len}(l)-d>\text{len}(r)\),这个时候说明上一次添加的是 \(r\),把 \(l\) 所欠的长为 \(d+\text{len}(r)\) 的前缀匹配了长为 \(\text{len}(r)\) 的后缀。同理先考虑是否能够匹配,如果可以应该加上 \(\sum_{r'<r}g(l,r',d+\text{len}(r))\)。
不难看出上面的转移可以用前缀和优化实现 \(O(1)\) 转移,总复杂度是 \(O(n\sum|s|)\) 的。
这个题的细节较多。首先说初始化,初始化需要考虑最终的回文串的对称中心的位置。如果在两个串之间,假设 \(\text{len}(l)>\text{len}(r)\),就是在 \(r\) 的反串是 \(l\) 的一个后缀的时候,\(f(l,r,\text{len}(l)-\text{len}(r))=1\)。如果在某一个串上,则需要考虑是以某一个字符为对称中心还是以两个字符的间隔为对称中心。接着因为加入字符串是有顺序的,假设目前两者已经完全匹配,那么先加入 \(l\) 和先加入 \(r\) 是本质相同的。为了区分这两种方案,我们钦定先加入 \(g\),也就是不会存在 \(l\) 完全没有匹配和 \(r\) 完全匹配的情况,注意判断即可。
CF1815E. Bosco and Particle
有一个很自然的想法是考虑每一行的循环节,然后去适应所有的行,让他们同时循环,于是我们先聚焦如何求出一行的循环节内容。
更具体来说,我们需要了解在一个循环节内,有多少次小球从 \(i-1\) 到了 \(i\),都多少次从 \(i\) 到了 \(i+1\),两者分别记为 \(a_i,b_i\),注意这里的一个循环节并不是字符串的循环,而是小球路径的循环。关注到小球会在形如 \(011\cdots110\) 的段里不断反弹,此时 \(a_i\) 会加 \(1\) 而 \(b_i\) 则增加 \(1\) 的个数加 \(1\);或者遇到 \(1\) 从而直接反弹,此时 \(a_i\) 的个数加 \(1\) 而 \(b_i\) 不变。考虑到每一次小球从到达 \(i\) 再回到 \(i-1\) 的过程一定遇到了偶数个 \(0\),这说明我们只需要找到原字符串的一个循环节,最多循环这个循环节两遍我们就可以得到一个小球路径的循环。找这个循环节可以用 KMP 做到 \(O(\sum|s|)\),注意这里的循环和周期有差异,因为循环的长度一定是原字符串的因数。
考虑求出 \(a_i,b_i\) 之后如何求解答案,我们考虑设小球有 \(f_i\) 次从 \(i\) 到达 \(i+1\),那么答案即为 \(2\sum_{0\le i\le n}f_i\),不难看出我们只需要找到最小的 \(\sum f\) 使得所有 \(f\) 都是整数即可。考虑 \(f\) 之间的比例关系,应该有 \(\frac{f_{i-1}}{f_i}=\frac{a_i}{b_i}\),因此有 \(f_i=\frac{b_i}{a_i}f_{i-1}\),以 \(f_0\) 作为主元就是 \(f_i=f_0\prod_{0\le k\le i}\frac{b_i}{a_i}\),因为所有 \(f_i\) 都是整数,所以只需要让 \(f_0\prod_{0\le i\le n}\frac{b_i}{a_i}\) 均为整数即可……吗?实则不然,考虑我们并不是只需要满足比例关系,而是需要确实从 \(f_{i-1}\) 中找出 \(k\) 组 \(a_i\),因此我们应当使得所有 \(f_0\frac{\prod_{0\le k<i}b_i}{\prod_{0\le k\le i}a_i}\) 为整数。我们可以维护所有 \(\frac{\prod_{0\le k<i}b_i}{\prod_{0\le k\le i}a_i}\) 的质因数分解,设 \(p\) 对应的指数为 \(\alpha_p\),那么 \(f_0\ge\prod p^{\max(0,-\alpha_p)}\),根据求得的最小 \(f_0\) 推出所有 \(f\) 后直接求解即可。复杂度是 \(O(n)\) 的。
CF1621G. Weighted Increasing Subsequences
考虑哪些点可以成为题目中的 \(x\) 且最优,显然是原序列的一些后缀非严格最大值,这样我们对原序列分层,对于权值在两个后缀非严格最大值 \(l,r\) 之间(\(l\le r\))的位置 \(u\),显然他对答案的贡献是包含 \(u\) 且结尾在 \(r\) 之前的上升子序列个数。进一步,因为 \(r\) 作为一个后缀非严格最大值,后面不可能有匹配的下一位,因此可以说答案就是包含 \(u\) 且结尾不为 \(r\) 的上升子序列个数。设 \(f_u\) 表示以 \(u\) 为开头的上升子序列个数,\(g_u\) 表示以 \(u\) 为开头的所有上升子序列的权值和,那么转移是容易的:
然而这个内容我们没有考虑结尾不包含 \(x\),因为给其他节点转移的时候 \(f\) 可以包含 \(x\) 而 \(g\) 不能包含 \(x\),所以我们可以先不加入 \(x\) 跑出正确的 \(g\),然后消除贡献加入 \(x\) 后跑出正确的 \(f\),用树状数组优化实现即可,复杂度是 \(O(n\log n)\) 的。
CF1326F2. Wise Men (Hard Version)
考虑对于每一个字符串单独求解是困难的,折半也只能做到 \(O(2^n{n\choose \frac{n}{2}}\text{poly}(n))\) 的复杂度毫无前途。于是我们考虑忽略某一个限制从而选择容斥,更具体的,我们令 \(\text{ans}_S\) 表示 \(S\) 是最终的答案串的子集的排列的方案数,那么只要求出所有 \(\text{ans}\),就可以用高维后缀差分的办法求出所有答案,复杂度是 \(O(n2^n)\) 的。
考虑怎样求解出 \(\text{ans}\) 数组。如果将给出的矩阵看作邻接矩阵,那么所有答案串 \(1\) 的连通块象征着原图的一条简单路径,那么一个答案串就可以用简单路径的合并得到,并且因为是钦定 \(S\),所以即使相邻的两条链可以连接也没有问题。那么对于每一个钦定的答案,我们可以求出需要的路径长度的简单集合 \(\{a_i\}\),满足 \(\sum a=n\),这样的集合的个数是 \(O(P(n))\) 的(\(P(n)\) 是 \(n\) 的拆分个数)。关于路径的信息可以通过设 \(f_{S,i}\) 表示经过的点集为 \(S\) 且停留在 \(i\) 的路径的方案数统计出图中所有不同的路径,这个转移可以简单做到 \(O(2^nn^2)\),然后设 \(g_{l,S}\) 表示长度为 \(l\) 且经过的点集为 \(S\) 的路径条数,在求出 \(f\) 后这个是容易求得的。
接着我们考虑所有路径对钦定的答案的影响,那么很明显要求所有的点集的并是全集,并且和集合中的长度一一对应。遇到点集并我们考虑 FMT,这样对于每一个集合,合并集合中的所有长度对应的 \(g\) 数组就可以得出并集为全集的方案数。然而考虑我们只关心全集的答案,所以我们可以最后不进行 IFMT,而是用容斥直接求出,复杂度可以做到 \(O(2^nS(n))\)(\(S(n)\) 是 \(n\) 的拆分的大小和)。这样所有内容都求解完后我们就可以在 \(O(2^n(S(n)+n^2))\) 的复杂度内完成求解。
CF1119F. Niyaz and Small Degrees/LG7600. [APIO2021] 封闭道路
考虑一个 naive 的简单树形 dp,我们可以枚举所有的 \(x\),然后设 \(f(u,\{0,1\})\) 表示 \(u\) 子树内所有的度数均 \(\le x\) 且点 \(u\) 和父亲有/没有连边时删边的最小代价和。那么转移考虑这一个点向子树内的度数和,如果到自己的父亲有边,那么子树内只能有 \(x-1\) 条连边,也就是说至少要删去 \(\text{deg}_u-x\) 条和子树内的连边才可以达到,否则至少要删除 \(\text{deg}_u-x-1\) 条边。我们考虑点 \(u\) 到子树的每一条边,从不删除到删除的代价从 \(f(v,0)\) 变成 \(f(v,1)+w\),因为想要让总代价尽可能低,所以我们想要让选择的边的数量不少于要求的数量,并且增加的代价总和尽可能低。这个过程可以贪心地先选择 \(f(v,1)+w<f(v,0)\) 的边,然后补充增加代价尽可能小的其他边,用优先队列可以简单做到。复杂度是 \(O(n^2\log n)\) 的。
考虑如何优化这个 dp。我们发现,如果 \(\text{deg}_u\le x\),那么我们不需要决策这个点也一定符合条件,也就是说我们可以将这个点视作叶子节点,然后对其他节点的连通块进行上述树形 dp 和将所有答案汇总即可,这样子之后所有 dp 的有效节点数是 \(O(n)\) 的。然而此时复杂度依然是 \(O(n^2\log n)\) 的,复杂度瓶颈在于优先队列的 \(O(\sum\text{deg}^2\log n)\) 的统计答案,和每一次枚举子树内边加入贡献的 \(O(n^2\log n)\),我们需要精细实现。
考虑对每个点连出去的边按照度数排序,这样当枚举到一个无用点后,剩余的点都是无用点,可以不用枚举,这样可以将插入有用点贡献的贡献降到 \(O(n\log n)\)。接着对于统计答案,我们考虑将无用点和有用点的贡献分开统计,更具体的,我们始终维护每一个点无用点的贡献,显然这个内容不随 \(x\) 的变化而变化,那么每一次只需要在里面新增有用点的贡献即可。当无用点个数 \(c\) 多于 \(\text{deg}_{u}-x\) 个,说明里面一定存在不优的内容,直接删去即可。此时单次的复杂度一定少于有用点的个数,总复杂度也是 \(O(n\log n)\) 的。具体实现可以用可删堆维护无用点贡献,统计完成后将每次新增的有用点贡献删掉。