dp problems
相关的算法可以看 dp tricks 那篇文章,这篇文章主要写题,并且记录一些常见的以我目前水平难以归类的东西。
[BJ United Round #3] 三色树
改编自 ProjectEuler #677。
请你对满足以下要求的 \(n\) 个节点的 无标号无根树 计数:
- 每个节点是三种颜色之一:红,蓝,黄
- 红色节点度数不超过 \(4\),蓝色和黄色节点度数均不超过 \(3\)
- 黄色节点不能相邻
注意 无标号无根树 的意义是:如果两颗树可以通过重新编号的方法使得对应点颜色相同,对应连边一致,则认为是同一颗树。
答案对输入的质数 \(p\) 取模。 \(n\leq 3000,9\times 10^8\leq p\leq 1.01\times 10^9\)
考虑无标号有根树怎么做。因为无编号,所以我只关心子树的大小。设 \(f(i,0/1/2)\) 表示大小为 i 的子树,根节点的颜色是红、黄、蓝,并且根节点会有父亲的方案数,为了满足度数的限制需要记 \(g(i,j)\) 表示 i 棵树总大小为 j 的方案数,为了满足黄点还要记 \(h(i,j)\) 表示 i 棵树总大小为 j 并且根节点都不是黄点的方案数。对于 f,有转移 \(f(i,0)=\sum_{j=0}^3 g(j,i-1),f(i,1)=\sum_{j=0}^2 g(j,i-1),f(i,2)=\sum_{j=1}^2h(j,i-1)\)。考虑 g 和 h 的转移。你考虑转移 \(f(i)\) 相当于是让森林多了许多大小为 \(i\) 的子树,所以 \(g\) 和 \(h\) 的转移也要跟 \(i,j\) 有关系。枚举我们往森林里面扔了多少颗大小为 \(i\) 的子树,转移系数相当于从 \(f(i,0)+f(i,1)+f(i,2)\) 中选出 \(k\) 种的方案数,这个方案数我们知道就是 \(\binom{k+f(i,0)+f(i,1)+f(i,2)-1}{k}\),有转移 \(g(x,y)=\sum_{k}g(x-k,y-ki)\binom{k+f(i,0)+f(i,1)+f(i,2)-1}{k}\);对于 \(h\) 的转移只需要把 \(f(i,2)\) 改掉,也就是 \(h(x,y)=\sum_{k}h(x-k,y-ki)\binom{k+f(i,0)+f(i,1)-1}{k}\),这样做就是 \(O(n^2)\) 的。
然后考虑无标号无根树怎么做。这里我们钦定树的重心为根,重心的每个儿子大小都不会超过 \(\lfloor\frac{n}{2}\rfloor\),根据这个性质我们用重心代表这棵树的结构,dp 的时候转移只做到 \(\lfloor\frac{n}{2}\rfloor\) 然后让后面的直接继承即可。然后枚举中心的颜色和度数,如果重心是红点那么答案就是 \(\sum_{i=0}^4 g(i,n-1)\),如果重心是蓝点那么答案就是 \(\sum_{i=0}^3 g(i,n-1)\),重心是黄点那么答案就是 \(\sum_{i=0}^3 h(i,n-1)\)。但是你发现这样样例是过不了的,因为一棵树大小为偶数的时候可能会有两个重心,会算重。根据重心的性质,另一个重心一定和当前的重心有一条边直接相连,相当于这条边将整棵树分成了两个大小为 \(t=\frac{n}{2}\) 的树,我们就考虑这两棵树。假设其中的树根没有黄色,那么答案就是 \(\binom{f(t,0)+f(t,1)}{2}\),如果存在一个黄色节点那么答案就是 \(f(t,2)(f(t,1)+f(t,0))\),把这两种方案数的和减去就是答案了。时间复杂度 \(O(n^2)\)。
但是上面一部分对于 EI 来说太简单了,他一笔就带过了。然后他提出了 \(O(n\log n)\) 的做法,但是我不会 Polya 技术定理。大哭。
[ZJOI2016] 小星星
小 Y 是一个心灵手巧的女孩子,她喜欢手工制作一些小饰品。她有 \(n\) 颗小星星,用 \(m\) 条彩色的细线串了起来,每条细线连着两颗小星星。
有一天她发现,她的饰品被破坏了,很多细线都被拆掉了。这个饰品只剩下了 \(n−1\) 条细线,但通过这些细线,这颗小星星还是被串在一起,也就是这些小星星通过这些细线形成了树。小 Y 找到了这个饰品的设计图纸,她想知道现在饰品中的小星星对应着原来图纸上的哪些小星星。如果现在饰品中两颗小星星有细线相连,那么要求对应的小星星原来的图纸上也有细线相连。小 Y 想知道有多少种可能的对应方式。
只有你告诉了她正确的答案,她才会把小饰品做为礼物送给你呢。
典中典。暴力 dp 是 \(dp(i,j,S)\) 表示 \(i\) 映射过去是 \(j\) 并且子树内的映射构成集合 \(S\) 的方案数,枚举子集你就炸了。这个题最厉害的就是容斥不重这个条件,你枚举 \(S\) 表示 \(S\) 中的点必须被映射到,\(O(n^3)\) dp 一下计算方案数,然后根据重复的数量确定容斥系数即可,时间复杂度 \(O(2^nn^3)\)。
[HAOI2011] problem a
一次考试共有 \(n\) 个人参加,可能出现多个人成绩相同的情况。第 \(i\) 个人说:“有 \(a_i\) 个人成绩比我高,\(b_i\) 个人成绩比我低。”请求出最少有几个人没有说真话。
如果 \(a_i+b_i+1>n\) 直接不可能是真话,然后你考虑剩下的人,\(a_i,b_i\) 相当于 \(a_i\) 个人比他高,\(n-a_i-b_i\) 个人和他一个分。如果 \(a_i,b_i\) 相同的人数大于 \(n-a_i-b_i\) 的话就只能保留 \(n-a_i-b_i\) 个。对于剩下的人问题就变成了 \(m\) 个区间,每个区间有价值 \(v_i\),求选出若干个不交的区间的最大价值,然后就是最大独立集问题了。
[HEOI2013] SAO
Welcome to SAO ( Strange and Abnormal Online)。这是一个 VR MMORPG, 含有 n 个关卡。但是,挑战不同关卡的顺序是一个很大的问题。
某款游戏有 \(n-1\) 个对于挑战关卡的限制,诸如第 \(i\) 个关卡必须在第 \(j\) 个关卡前挑战,或者完成了第 \(k\) 个关卡才能挑战第 \(l\) 个关卡。并且,如果不考虑限制的方向性,那么在这 \(n-1\) 个限制的情况下,任何两个关卡都存在某种程度的关联性。即,我们不能把所有关卡分成两个非空且不相交的子集,使得这两个子集之间没有任何限制。
如果树是外向树那么答案就是 \(\frac{n!}{\prod siz_x}\),但是现在有了内向边,所以我们把内向边容斥掉,设 \(dp(i,j)\) 表示 \(i\) 的子树里面 \(j\) 条内向边被钦定不满足的方案数,但是你还要拓扑序计数所以你需要记 \(k\) 表示 \(i\) 所在的联通块大小,状态 \(O(n^3)\) 炸了,发现我们最后要求 \(\sum dp(i,j,k)(-1)^j\),直接设 \(f(i,k)\) 表示带上容斥系数的方案数,转移是树形背包状物,时间复杂度 \(O(n^2)\)。
[HNOI2008] 玩具装箱
【模板】斜率优化 dp。但是你让我场上写我肯定写李超线段树。
Luogu P6047 丝之割
【模板】斜率优化 dp。但是你让我场上写我肯定写李超线段树。
Luogu P4321 随机漫游
H 国有 N 个城市。在接下来的 M 天,小 c 都会去找小 w,但是小 c 不知道小 w 的具体位置,所以小 c 决定每次随机找一条路走,直到遇到了小 w 为止。小 c 知道小 w 只有可能是在 \(c_1,c_2,\dots,c_n\) 这 n 个城市中的一个,小 c 想知道在最坏情况下,小 c 遇到小 w 期望要经过多少条道路。H 国所有的边都是无向边,两个城市之间最多只有一条道路直接相连,没有一条道路连接相同的一个城市。\(n\leq 18,m\leq 100000\)。
最差的情况就是把 \(c\) 都走一遍才找到。发现 \(n\) 很小,考虑维护 \(dp(u,S)\) 表示走到了 \(u\),已经走到了 \(S\) 中的节点的期望,我们有转移 \(dp(u,S)=\frac{1}{deg(u)}\sum_{(u,v)\in E} dp(v,S|v)+1\),发现这样需要对 \(n2^n\) 个方程做高斯消元,爆炸了。这道题最厉害的是考虑 \(S|v\) 这个东西,陶一下有 \(dp(u,S)=\frac{1}{deg(u)}(\sum_{(u,v)\in E,v\in S}dp(v,S)+\sum_{(u,v)\in E,v\not\in S}dp(v,S|v))+1\),然后发现我们可以按集合大小倒推求 dp,对于每个状态只用列 \(n\) 个方程,时间复杂度 \(O(2^nn^3)\)。
[NOI2007] 生成树计数
一天,小栋和同学聚会,大家围坐在一张大圆桌周围。小栋看了看,马上想到了生成树问题。如果把每个同学看成一个结点,邻座(结点间距离为 \(1\))的同学间连一条边,就变成了一个环。可是,小栋对环的计数已经十分娴熟且不再感兴趣。于是,小栋又把图变了一下:不仅把邻座的同学之间连一条边,还把相隔一个座位(结点间距离为 \(2\))的同学之间也连一条边,将结点间有边直接相连的这两种情况统称为 有边相连。小栋以前没有计算过这类图的生成树个数,但是,他想起了老师讲过的计算任意图的生成树个数的一种通用方法:构造一个 \(n\times n\) 的矩阵 \(A=\{ a_{i,j}\}\),其中:
\[a_{i,j}=\begin{cases} d_i & i=j \\ -1 & \text{$i, j$ 之间有边直接相连} \\ 0 & \text{其他情况} \end{cases}\]为了计算所对应的生成数的个数,只要去掉矩阵 \(A\) 的最后一行和最后一列,得到一个 \((n-1)\times(n-1)\) 的矩阵 \(B\),计算出矩阵 \(B\) 的行列式的值便可得到图 1 的生成树的个数。小栋发现利用通用方法,因计算过于复杂而很难算出来,而且用其他方法也难以找到更简便的公式进行计算。于是,他将图做了简化,从一个地方将圆桌断开,这样所有的同学形成了一条链,连接距离为 1 和距离为 2 的点。这样生成树的总数就减少了很多。小栋不停的思考,一直到聚会结束,终于找到了一种快捷的方法计算出这个图的生成树个数。可是,如果把距离小于等于 \(k\) 的点也连起来,小栋就不知道如何快捷计算了。现在,请你帮助小栋计算这类图的生成树的数目。\(n\leq 10^{15},2\leq k\leq 5\)。
严肃根据题面学习矩阵树定理,随即考虑生成树计数我只会这个,所以只能嗯做了。行列式这个东西比较方便的就是上三角矩阵的行列式是主对角线上元素的乘积。你发现这个矩阵在 \(k+1\) 到 \(n-k\) 行每一行都是 \(k\) 个 \(-1\),一个 \(2k\) 和 \(k\) 个 \(-1\) 拼起来的,把这几行顶上的几行扔下去,矩阵大概就长这个样子:
然后前几行和最后几行就构成了上三角矩阵,把最后几行扔到上面去,行列式就是 \((-1)^{n-k}\),注意这里我们做了若干次交换操作所以要乘上若干个 -1。对于剩下的几行考虑用前面的消成上三角矩阵,对于一个行向量消第 \(k\) 次的时候加上 \(a_0\) 数乘第 \(k\) 行的行向量这样做一次前面就多一个 0,发现这样消元的次数爆炸了,但是发现消元可以写成向量乘矩阵形式,矩阵快速幂优化即可,这样时间复杂度就是 \(O(k^4\log k)\) 的,算上之前的各种消元还是这个。
主播主播这个做法还是太吃操作了有没有不吃操作的做法?
有的兄弟有的。考虑 dp 到 \(i\) 的时候维护一下和 \([i-k,i-1]\) 这些点的连通性 \(S\),这个 \(S\) 考虑第 \(i\) 个点维护属于哪个集合,状态爆炸。这里使用最小表示法的思想,就是说假设当前最大的集合编号是 \(i\),那么下一个点的集合编号最大只能是 \(i+1\),发现这些集合就能维护所有的状态并且 \(k=5\) 的时候状态数最多就是 \(52\),我们预处理出来 \(g(S,T)\) 表示从 \(S\) 转移到 \(T\) 的方案数,然后 \(dp(i,S)=\sum dp(i-1,T)g(S,T)\),这个东西可以矩阵优化。
[POI 2007] 堆积木KLO
clz 从他的 npy 那里得到了一个由 \(n\) 块积木叠成的高塔,每块积木上都写有一个数字。我们记从下往上第 \(i\) 块积木上面的数为 \(a_i\),将一个满足积木上的数为 \(a_1,a_2,\dots,a_n\) 的高塔用 \(\{a_1,a_2,\dots,a_n\}\) 直接表示,则 clz 认为高塔 \(\{a_1,a_2,\dots,a_m\}\) 价值为 \(\sum_{i=1}^m [a_i = i]\)。
clz 可以删除当前高塔中的若干个积木,其余的积木受重力影响会下落到不能下落为止。如果将高塔 \(\{1,1,2,4,5\}\) 中从下往上第二个积木删去,那么可以得到高塔 \(\{1,2,4,5\}\),新高塔的价值为 \(2\)。
clz 想删除当前高塔中任意个积木,使得最终得到的高塔价值最大。由于他是人赢,所以他指定你来回答这个问题。
\(dp(i,j)\) 表示前 \(i\) 个剩了 \(j\) 个,然后你发现扔到 \([a_i=j]\) 的限制的话能转移到 \((i,j)\) 的二元组扔平面直角坐标系上是一个平行四边形,把平行四边形改成矩形,写出约束来是一个带修改的二维偏序问题,CDQ 分治维护就可以了。
[NOI2015] 寿司晚宴
在晚宴上,主办方为大家提供了 \(n−1\) 种不同的寿司,编号 \(1,2,3,\ldots,n-1\),其中第 \(i\) 种寿司的美味度为 \(i+1\)。(即寿司的美味度为从 \(2\) 到 \(n\))
现在小 G 和小 W 希望每人选一些寿司种类来品尝,他们规定一种品尝方案为不和谐的当且仅当:小 G 品尝的寿司种类中存在一种美味度为 \(x\) 的寿司,小 W 品尝的寿司中存在一种美味度为 \(y\) 的寿司,而 \(x\) 与 \(y\) 不互质。
现在小 G 和小 W 希望统计一共有多少种和谐的品尝寿司的方案(对给定的正整数 \(p\) 取模)。注意一个人可以不吃任何寿司。\(n\leq 500\)。
互质这个东西拆成质因数考虑,相当于小 G 选的质因数不能被小 B 选过,考虑 \(dp(S,T)\) 表示小 G 选了 \(S\) 内的质因数,小 B 选了 \(T\) 内的质因数的方案数,扫 \(i\) 转移,但发现 500 以内的质数个数有 95 个,这样做炸了,但是考虑到从 23 往上的质数的次数和最多是 1,所以暴力的 \(S\) 和 \(T\) 都是 \(2^8=256\) 的可以直接维护,然后对于每个数按最大的质因数排序,这样连续的一段最大最大质因数相等的数就只能选 1 了。每次进入一个连续段的时候我们算 \(f(S,T)\) 表示只让小 G 选的方案数,\(g(S,T)\) 表示小 W 选的方案数,走出这一段之后让 \(dp(S,T)\leftarrow f(S,T)+g(S,T)-dp(S,T)\),这样做就是 \(O(n4^8)\) 的,可以通过这道题目。
一个优化是发现 \(S\) 和 \(T\) 是不交的,所以可以通过枚举子集做到 \(O(n3^8)\)。
[CQOI2012] 局部极小值
有一个 \(n\) 行 \(m\) 列的整数矩阵,其中 \(1\) 到 \(n\times m\) 之间的每个整数恰好出现一次。如果一个格子比所有相邻格子(相邻是指有公共边或公共顶点)都小,我们说这个格子是局部极小值。给出所有局部极小值的位置,你的任务是判断有多少个可能的矩阵。答案对 \(12345678\) 取模。\(1\le n\le4\),\(1\le m\le7\)。
显然的情况是如果有两个 X 八连通那方案数为 0。你考虑填值域,维护当前填了的点。设 \(dp(i,S)\) 表示考虑到值域 \(i\),当前填了 \(S\) 内的点的方案数,考虑对 \(S\) 的约束相当于不能存在一个没填的 X 旁边填了数,所以 \(i\) 要么填在一个 X 上,要么填在一个填了数的 X 的旁边,你不能填在一个谁都管不到的地方因为这样的话 \(i\) 会成为一个局部极小值而这不符合要求。设 \(c=nm\),这样时间复杂度就是 \(O(c^22^c)\) 的,恭喜你炸掉了。发现影响我们的是“恰好这些地方是局部极小值”的限制,于是容斥,考虑容斥是往子集上走还是往超集上走,这里肯定是超集因为影响答案的是超集。钦定 \(U\) 内的点是局部极小值,在这个基础上设 \(dp(i,S)\) 表示填到 \(i\) 然后局部极小值走到了 \(S\) 的方案数,统计答案的时候按 \(|U|\) 容斥,直接枚举你的复杂度还是炸的,但是考虑 \(U\) 需要合法,并且 \(|U|\) 最多是 8,这样一来你就赢了。而且你发现你根本不用枚举 \(U\),只需要把所有 \(dp(nm,S)\) 中的 \(S\) 看成 \(U\) 就行了!
Luogu P3600 随机数生成器
sol 研发了一个神奇的随机数系统,可以自动按照环境噪音生成真·随机数。
现在 sol 打算生成 \(n\) 个 \([1,x]\) 的整数 \(a_1, ..., a_n\),然后进行一些询问。
\(q\) 次询问,每次询问 \(i\) 有两个参数 \(l_i\) 和 \(r_i\),sol 会计算 \(\min_{l_i \leq j \leq r_i} a_j\)(\(a\) 数组中下标在 \(l_i, r_i\) 之间的数的最小值)。
最后测试结果会是这些询问得到的结果的最大值。sol 进行了很多次实验,现在他想问问你测试结果的期望大小是多少,对 \(666623333\) 取模。\(1 \leq n,x,q \leq 2000\)
你发现期望很难算,所以尝试计算概率。设 \(p(i)\) 表示答案是 \(i\) 的概率,答案是 \(\sum p(i)i\)。最大值恰好为 \(i\) 这个东西对于取最大值/最小值这种东西很乏力,考虑记 \(g(i)\) 表示最大值小于等于 \(i\) 的概率,那 \(p(i)=g(i)-g(i-1)\),然后把大于 \(i\) 的数看成 1,小于 \(i\) 的数看成 0,成为 1 的概率就是 \(\frac{x-i}{x}\),成为 0 的概率就是 \(\frac{i}{x}\),然后取 min 得 1 相当于全是 1,概率是 \(\frac{i}{x}\cdot len\),如果所有区间存在一个 1 那么 \(ans>x\),如果全是 0 那么 \(ans<x\),太难了。再转化一步,\(g(i)=c(i)/(x^n)\),也就是记 \(c(i)\) 表示最大值小于等于 \(i\) 的方案数,这相当于没有一个区间答案是 1 的方案数,相当于每个区间都有至少一个 0 的方案数。设 \(s(i)\) 表示填了 \(i\) 个 0 并且每个区间都是 0 的方案数,那么 \(c(i)=\sum s(j)(i^j\cdot (x-i)^{n-j})\)。然后这个东西比较常见了,设 \(dp(i,j)\) 表示前 \(i\) 个位置放了 \(j\) 个 0,第 \(i\) 位放 0 的方案数,考虑转移一定是 \(dp(i,j)\leftarrow dp(k,j-1)\) 并且 \(k\) 和 \(i\) 两个点能够把 \((k,i)\) 内所有的区间都放上一个 1,这相当于不存在区间左端点右端点均在 \((k,i)\) 之间,发现 \(k\) 的取值是从 \(i-1\) 开始的一段区间,可以二分出这个 \(k\),然后对于这一段值求和,时间复杂度就是 \(O(n^2\log n)\) 的,但是好像不优化直接暴力跳也能过?现在不可以了!这样 \(s(i)=\sum dp(j,i)\),也就是枚举最后一个放 \(0\) 的位置,回带就能求出答案了。
[PKUWC2018] Minimax
小 \(C\) 有一棵 \(n\) 个结点的有根树,根是 \(1\) 号结点,且每个结点最多有两个子结点。
定义结点 \(x\) 的权值为:
1.若 \(x\) 没有子结点,那么它的权值会在输入里给出,保证这类点中每个结点的权值互不相同。
2.若 \(x\) 有子结点,那么它的权值有 \(p_x\) 的概率是它的子结点的权值的最大值,有 \(1-p_x\) 的概率是它的子结点的权值的最小值。
现在小 \(C\) 想知道,假设 \(1\) 号结点的权值有 \(m\) 种可能性,权值第 \(i\) 小的可能性的权值是 \(V_i\),它的概率为 \(D_i(D_i>0)\),求:
\[\sum_{i=1}^{m}i\cdot V_i\cdot D_i^2 \]你需要输出答案对 \(998244353\) 取模的值。\(1\leq n\leq 3\times 10^5\),\(1\leq w_i\leq 10^9\)。
式子有个 \(D_i^2\) 看起来要组合意义转化,但是根本用不着!!!
朴素的设 \(dp(i,j)\) 表示节点 \(i\) 的排名是 \(j\) 的概率,有转移
然后这个东西我们在单点上维护 \(dp(i,j)\) 和 \(\sum dp(i,j)\),然后线段树合并做两次再加起来就可以了!
[ZJOI2012] 波浪
阿米巴和小强在大海旁边看海。小强建立了一个模型。海面抽象成一个 \(1\) 到 \(N\) 的排列 \(P_{1\ldots N}\)。定义波动强度等于相邻两项的差的绝对值的和,即:
\[L = | P_2 – P_1 | + | P_3 – P_2 | +\ldots + | P_N – P_{N-1} | \]给你一个 \(N\) 和 \(M\),问:随机一个 \(1\ldots N\) 的排列,它的波动强度不小于 \(M\) 的概率有多大?
摩 天 大 楼。
算了还是写一下毕竟忘得差不多了。首先题目相当于是求 \(L\geq M\) 的方案数。维护序列显然不可做,考虑扫值域,假设扫到了第 \(i\) 个数,考虑我们填好的东西形成的是一些连续段,我们要把 \(i\) 放到连续段的两边或者是新开一个连续段。维护 \(dp(i,j,k,0/1/2)\) 填到 \(i\),形成了 \(j\) 个连续段,产生 \(k\) 的代价,位置 1 和位置 n 填了几个,直接转移就做完了。这题甚至比摩天大楼还不用脑子。
[八省联考 2018] 林克卡特树
游戏中有一个叫做 LCT 的挑战,它的规则是这样子的:现在有一个 \(N\) 个点的树,每条边有一个整数边权 \(v_i\),若 \(v_i \geq 0\),表示走这条边会获得 \(v_i\) 的收益;若 \(v_i \lt 0\) ,则表示走这条边需要支付 \(-v_i\) 的过路费。小 L 需要控制主角 Link 切掉(Cut)树上的恰好 \(K\) 条边,然后再连接 \(K\) 条边权为 0 的边,得到一棵新的树。接着,他会选择树上的两个点 \(p,q\),并沿着树上连接这两点的简单路径从 \(p\) 走到 \(q\),并为经过的每条边支付过路费/ 获取相应收益。
海拉鲁大陆之神 TemporaryDO 想考验一下 Link。他告诉 Link,如果 Link 能切掉合适的边、选择合适的路径从而使 总收益 - 总过路费 最大化的话,就把传说中的大师之剑送给他。
小 L 想得到大师之剑,于是他找到了你来帮忙,请你告诉他,Link 能得到的 总收益 - 总过路费 最大是多少。
题目转化为选 \(k+1\) 条不相交链让边权和最大。树上问题考虑利用子树这一子结构,设 \(dp(u,i,0/1/2)\) 表示考虑到子树 \(u\),选了 \(i\) 条不相交链,点 \(u\) 的度数是多少的最大边权和,然后暴力转移是容易的树上背包,然后你发现你不会了,然后你打了个表,然后你发现答案关于 \(i\) 是凸的,然后你利用 wqs 二分的套路,二分斜率 \(mid\),然后现在没有了背包容量的限制,所以可以简单的设 \(dp(u,0/1/2)\) 表示考虑子树 \(u\),度数是 \(0/1/2\) 的最大边权和,同时为了统计答案还需要记 \(c(u,0/1/2)\) 表示 \(dp(u,0/1/2)\) 对应的链的数量,二分 check 的条件就是 \(c(1,0/1/2)\geq k\)。
我们来证明凸性。设 \(f(k)=\max_{op=0}^2\{dp(1,0,op)\}\),我们要证明 \(f(k)\) 是凸的,首先要明确 \(\Delta f(k)\) 的单调性,发现对于 \(k=1\),显然有 \(2f(1)\geq f(2)+f(0)\),也就是 \(\Delta f(0)\geq \Delta f(1)\),于是 \(\Delta f\) 是单调递减的,尝试证明之。严格地,我们使用第二数学归纳法,记 \(g(k)=\Delta f(k)\),假设对于 \(k\leq n\) 都满足 \(g(k)\leq g(k-1)\),然后考虑 \(k+1\) 条路径应该怎么选。感性理解不难,就是说我选了 \(k\) 条之后能选的和选了 \(k-1\) 条之后能选的相比肯定要少,所以增量也就少。考虑严格证明,也就是我不会的东西。
首先我们证明一个引理:\(f(k)\) 所对应的 \(k\) 条路径的端点一定在 \(f(k+1)\) 所对应的 \(k+1\) 条路径的端点中出现。证明考虑反证法,假设 \(k+1\) 条路径的端点中有不是原来 \(k\) 条路径端点的,那么至少有两条这样的路径,如果这两条路径与原来没被包含的那条路径 \(g\) 有交,可以发现对于这两个路径,把每个路径的一个端点变成 \(g\) 的端点中的一个一定更有;如果与原来无交的话那把路径改成 \(g\) 一定更优,证毕。
有了这个引理,感性理解中 "能选的要少" 就量化了,命题也就自然得证。
[NOI2016] 国王饮水记
跳蚤国有 \(n\) 个城市,伟大的跳蚤国王居住在跳蚤国首都中,即 \(1\) 号城市中。
跳蚤国最大的问题就是饮水问题,由于首都中居住的跳蚤实在太多,跳蚤国王又体恤地将分配给他的水也给跳蚤国居民饮用,这导致跳蚤国王也经常喝不上水。
于是,跳蚤国在每个城市都修建了一个圆柱形水箱,这些水箱完全相同且足够高。一个雨天后,第 \(i\) 个城市收集到了高度为 \(h_i\) 的水。由于地理和天气因素的影响,任何两个不同城市收集到的水高度互不相同。
跳蚤国王也请来蚂蚁工匠帮忙,建立了一个庞大的地下连通系统。跳蚤国王每次使用地下连通系统时,可以指定任意多的城市,将这些城市的水箱用地下连通系统连接起来足够长的时间之后,再将地下连通系统关闭。由连通器原理,这些城市的水箱中的水在这次操作后会到达同一高度,并且这一高度等于指定的各水箱高度的平均值。
跳蚤国王至多只能使用 \(k\) 次地下连通系统。跳蚤国王请你告诉他,首都 \(1\) 号城市水箱中的水位最高能有多高?
首先 \(h_i\leq h_1\) 的直接扔掉,然后观察到每个城市最多被用一次,并且如果没有次数限制的话一个一个合并肯定是更优的,有限制的话每次也合并的是连续的一段,套路地设 \(dp(i,j)\) 表示 \(i\) 作为一个连续段的结尾选了 \(j\) 段的答案,转移式子是 \(dp(i,j)\leftarrow \frac{dp(k,j-1)+s(i)-s(k)}{i-k+1}\),可以吧 \(j\) 这一维扔掉,你发现这个东西长得很像斜率的形式,把分子上 \(k\) 相关的放一块,有 \(\frac{s(i)-(s(k)-dp(k))}{i-(k-1)}\),相当于是把 \((k,s(k)-dp(k))\) 看成一个点,我们要让 \((i,s(i))\) 和这个点的连线的斜率尽可能大,发现有用的点构成了一个凸包,并且答案是严格单峰的,所以直接在凸包上三分即可,看起来过了,但是他给的高精度小数有 \(O(P)\) 的时间复杂度,所以我们需要一个更优的算法。我们发现决策具有单调性,于是时间复杂度变成了 \(O(nkP)\),还是过不了。事实上这里只需要在 dp 的时候用 long double 存,并且记一下前面从哪过来的,最后统计答案就可以了,\(P\) 就被扔了,你就赢了,但是这并不是最优的解法
然后你发现 \(h_i\) 互不相等,那么你考虑我越往后我的连续段的长度肯定就要越短。你发现这并没有什么用,但是大神们得出了一个更厉害的结论:长度大于 1 的区间个数不超过 \(\log_2\frac{nh}{\min\{h_i-h_{i-1}\}}\),然后你只用 dp 14 层就行了!非常厉害!
卡空间那句话是吓唬人的,实际上我也就写了 14K。https://paste.ubuntu.com/p/RBQRpFmMkF/
[ZJOI2016] 线段树
小 Yuuka 遇到了一个题目:有一个序列 \(a_1,a_2,\ldots,a_n\),\(q\) 次操作。每次操作把一个区间内的数改成区间内的最大值,问最后每个数是多少。小 Yuuka 很快地就使用了线段树解决了这个问题。于是充满智慧的小 Yuuka 想,如果操作是随机的,即在这 \(q\) 次操作中每次等概率随机地选择一个区间 \([l,r]\)(\(1 \leq l \leq r \leq n\)),然后将这个区间内的数改成区间内最大值(注意这样的区间共有 \(\frac{n(n+1)}{2}\) 个),最后每个数的期望大小是多少呢?小 Yuuka 非常热爱随机,所以她给出的输入序列也是随机的(随机方式见数据规模和约定)。对于每个数,输出它的期望乘 \(\left(\frac{n(n+1)}{2} \right)^q\) 再对 \(10^9+7\) 取模的值。
对于所有的测试数据,保证序列中数的大小不超过 \(10^9\),并且每个数是 \(0\) 到 \(10^9\) 之间的随机整数。
有点像随机数生成器这个题。显然对于这个题每个点的答案都是独立的,离散化,维护 \(dp(i,j)\) 表示第 \(i\) 个数最终的答案小于等于 \(j\) 的方案数,等于 \(j\) 的方案数做一遍差分就可以了。你考虑 \((i,j)\) 相当于包含了 \(i\) 的区间的最大值都小于等于 \(j\),假设 \(q\) 次询问中有 \(k\) 次包含了 \(i\),那么这 \(k\) 次的 \(l\) 和 \(r\) 的取值范围都是确定的,统计一下就可以了,最后对 \(k\) 求和,时间复杂度是 \(O(n^3)\) 的。
好的,上面的做法是错的!因为你随便 \([l,r]\) 的时候可能他已经被前面的 \(\max\) 整的面目全非了,所以直接算是不可以的,并且显然要扫 \(q\) 这一维。仍然是求 \(\leq j\) 的方案数,考虑维护 \(x\) 表示做了 \(x\) 次操作,以及 \((l,r)\) 表示小于等于 \(j\) 的区间变成了 \((l,r)\) 的方案数,有转移 \(dp(x,l,r)\leftarrow dp(x-1,l,r)\),\(dp(x,l,r)\leftarrow dp(x-1,j,r)(j-1)\),\(dp(x,l,r)\leftarrow dp(x-1,l,j)(n-j)\),第一个式子的系数我省了。这样做的话是 \(O(n^4)\) 的,你写了一发想拼个小分发现过了因为数据是随的,但是我们还是希望一个优化。考虑算答案的时候我 \(jdp(x,l,r)\) 的贡献是 \((v_j-v_{j-1})dp(x,l,r)\),厉害的做法是我们直接把 \(j\) 扔到答案里面,维护 \(f(i,l,r)=\sum dp(x,l,r)\) 就赢了!
这里加入一点对于状态简化的瞎写。被简化的状态一般满足:这个状态不影响转移的子结构,并且我们不关心其具体值,也就是说这个状态不管怎么着都要扔到答案里,那就扔到答案里,不在状态上浪费空间了。例如这个题 \(j\) 这一维你发现转移的时候根本用不到,并且所有的 \(j\) 的贡献都要被扔到答案里,所以这一维就去答案里了。再比如 [ABC281G] 这个题,对于层数这一维,我们发现层数不管是多少都要扔到答案里面,并且转移的时候不依赖这个东西,所以这个状态就可以被扔掉了。你一般是把式子列出来然后判断哪一位对转移没有影响,大概率这一维就要被扔掉了。
[九省联考 2018] 秘密袭击 coat
C 国即将向 D 国发动一场秘密袭击。作战计划是这样的:选择 D 国的 \(s\) 个城市,派出 C 国战绩最高的 \(s\) 个士兵分别秘密潜入这些城市。每个城市都有一个危险程度 \(d_i\)。
C 国指挥官会派遣战绩最高的士兵潜入所选择的城市中危险程度最高的城市,派遣战绩第二高的士兵潜入所选择的城市中危险程度次高的城市,以此类推(即派遣战绩第 \(i\) 高的士兵潜入所选择城市中危险程度第 \(i\) 高的城市)。D 国有 \(n\) 个城市,\(n - 1\) 条双向道路连接着这些城市,使得这些城市两两之间都可以互相到达。为了任务执行顺利,C 国选出的 \(s\) 个城市中,任意两个所选的城市,都可以不经过未被选择的城市互相到达。
clz 操控的士兵的战绩是第 \(k\) 高,他希望能估计出最终自己潜入的城市的危险程度。Access Globe 假设 C 国是以等概率选出任意满足条件的城市集合 \(S\),他希望你帮他求出所有可能的城市集合中,Access Globe 操控的士兵潜入城市的危险程度之和。如果选择的城市不足 \(k\) 个,那么 clz 不会被派出,这种情况下危险程度为 \(0\)。
当然,你并不想帮他解决这个问题,你也不打算告诉他这个值除以 \(998\,244\,353\) 的余数,你只打算告诉他这个值除以 \(64\,123\) 的余数。
暴力树上背包是容易的,设 \(dp(x,u,c)\) 表示 clz 到达的危险程度小于等于 \(x\),考虑到子树 \(u\),选了 \(c\) 个小于等于 \(x\) 小的,差分可以找到危险程度是 \(x\) 的答案,时间复杂度 \(O(n^2k)\)。据说有人大力过了???
然后,经典的,树上背包是关于 \(c\) 的卷积,考虑维护生成函数。设 \(F(i,j)\) 是 \(i\) 的子树内值小于等于 \(j\) 的方案数,因为最后要求整棵树的 \(F(i,j)\) 的和,所以在设一个 \(G(i,j)=\sum_{x\in \operatorname{subtree}(i)}F(x,j)\),直接多项式卷积转移是极其慢的,利用 FFT 的思路转化成点值的问题(虽然这里你显然不能 FFT),然后每一位上的运算就是简单的乘法,每次转移的时候,有 \(F(i,j)\leftarrow F(i,j)\times (F(son,j)+1)\),\(G(i,j)\leftarrow G(i,j)+G(son,j)\),最后 \(G(i,j)\leftarrow G(i,j)+F(i,j)\),然后拉格朗日插值法求出 \([z^k]\)。考虑这个东西怎么维护,你显然不能给每个节点都维护这个 poly,但是我们可以利用 ddp 的思想直接维护转移矩阵,这样子树合并就可以当成线段树合并去做了。要注意复合的时候的顺序,矩阵乘法应该是左乘还是右乘。
[JOISC 2020] 最古の遺跡 3
JOI 教授是一名研究 IOI 王国的历史学家。他发现了一行古代石柱的废墟及一份古代文献。
古代文献上的记载如下:
- 刚建造完成的时候,有 \(2\times N\) 个石柱,对于 \(1\le k\le N\) 均有两个石柱高度为 \(k\),同时记第 \(i\) 个石柱的高度为 \(h_i\)。
- 会发生 \(N\) 次地震,每次地震会使一些石柱的高度 \(-1\),其他石柱高度不变。
- 石柱 \(i\) 地震时高度不变,当且仅当 \(h_i\ge 1\) 并且对于 \(j>i\) 都要有 \(h_i\not=h_j\)
- \(N\) 次地震后,恰好只剩下了 \(N\) 个石柱。
现在 JOI 教授找出了仅存的 \(N\) 个石柱的位置 \(A_1,A_2,\ldots,A_N\),他想让你求出,最初 \(2\times N\) 个石柱高度的修建方案数 \(\bmod~10^9+7\) 的值。
时间从后往前做显然是不可行的,考虑时间从前往后,从初始状态如何变成最终状态。发现地震的限制很神秘,但还是逃不开值和位置两个维度。考虑按值做,相当于是我给每个值 \(i\) 都维护一个有序的集合,每次地震把最右边的扔掉插入到 \(i+1\) 对应的集合里面,插入操作是难以维护的,因为你不知道你插入到 \(i+1\) 之后什么时候会出去。所以你考虑在初始序列上做这个问题,而且根据题目的限制你显然要从后往前做,我感觉这里最厉害的是这里直接扔掉时间,假设扫到 \(i\),就相当于后面的都已经珍完 \(n\) 轮了,假设当前高度为 \(h\),他会被震没相当于后面静态的出现了高度为 \(h\) 到 \(1\) 的柱子,直接维护 \(dp(i,h)\),其中 \(h\) 是最大的 \(h\),假设后面震没了的个数为 \(c0\),剩下的个数为 \(c1\),如果当前被震没了,那么 \(dp(i,h)\leftarrow dp(i+1,h)\cdot (j-c0)\);如果当前 \(i\) 被保留,如果 \(i\) 震完了的的高度为 \(h+1\),可能会将两端连续上升的 \(h\) 接起来,枚举 \(h'\),你需要先选出 \(h'-h-1\) 个位置,然后对于 \(i\) 你震之前有 \(h'-h+1\) 种取值,另外这 \(h'-h-1\) 个里面可能是被震小变成 \([h+2,h']\) 中的高度的,这个很不好求,考虑另计 \(g(x)\) 表示形成高度度为连续的长度为 \(x\) 的柱子的方案数,可以列出转移 \(dp(i,h')\leftarrow dp(i+1,h)\cdot \binom{c1-h}{h'-h-1}\cdot (h'-h+1)\cdot g(h'-h-1)\),如果 \(i\) 震完的高度大于 \(h+1\),那这根柱子会在刚才那种情况里产生贡献,这里先不管,直接 \(dp(i,h)\leftarrow dp(i+1,h)\)。然后考虑算 \(g(i)\),枚举最后被选出来的高度 \(j\),对于 \(\leq j-1\) 和 \(\geq j+1\) 的部分,他们互相是不相干的,再考虑位置就有 \(\binom{i-1}{j-1}\) 的贡献,然后考虑产生 \(j\) 的情况,我们有 \(2(i-j+1)\) 种情况可以选,有 \(i-j\) 个高度已经被选了,还剩 \((i-j+2)\) 种方案,所以 \(g(i)\leftarrow g(j-1)\cdot g(j+1)\cdot(i-j+1)\cdot\binom{i-1}{j-1}\) 就做完了,时间复杂度 \(O(n^3)\)。
[APIO2021] 封闭道路
在泗水市,有 \(N\) 个路口(编号从 \(0\) 到 \(N-1\))。这些路口由 \(N-1\) 条双向道路连接(编号从 \(0\) 到 \(N-2\)),因此通过这些道路,任意一对路口之间都有一条唯一的路径。\(i\) 号道路(\(0 \le i \le N-2\))连接着 \(U[i]\) 号和 \(V[i]\) 号路口。
为了提高环保意识,泗水市长 Pak Dengklek 计划举办无车日。为了鼓励该活动,Pak Dengklek 将组织封路。Pak Dengklek 将首先选择一个非负整数 \(k\),然后封闭一些道路,以使每个路口只能直接连接至多 \(k\) 条未封闭的道路。封闭 \(i\) 号道路的成本为 \(W[i]\)。
请你帮助 Pak Dengklek 对每个可能的非负整数 \(k\)(\(0 \le k \le N-1\))计算封闭道路的最低总成本。
好困难。
暴力 dp 就是设 \(dp(i,0/1)\) 表示考虑 \(i\) 的子树,钦定 \((i,fa)\) 这条边选不选的方案数,对于子树到当前点的转移我们对子树贪心地贡献即可。考虑优化,发现对于 \(\text{deg}\leq k\) 的点,它往儿子连的边都不用选,标记这些点是无用的,然后只对有用点转移,无用点如果和有用点直接相连那么贡献就是这条边的边权,但是直接做的话时间复杂度是假的,一个菊花就给你卡死了,发现 dp 的过程相当于给每个点维护了一个有序的结构,我们考虑用一个数据结构维护有用点的可用选择,支持插入和删除以及有序查询,发现只需要写一个堆就可以了,难后算复杂度,你发现每个点成为有用点的次数为 \(\text{deg}\),有用点 dp 的时间复杂度就是 \(\sum\text{deg}\log\text{deg}=\Theta(n\log n)\),然后对于原来是有用点现在是无用点的点直接枚举出边就行了,时间复杂度是 \(\sum\text{deg}\) 也是 \(O(n)\) 的,总时间复杂度 \(O(n\log n)\) 的。
[JSOI2010] 旅行
WJJ 事先得到了一张地图,上面标注了 \(N\) 个小动物的聚居地,也就是一个个的小村落。其中第 \(1\) 个村庄是 WJJ 现在住的地方,第 \(N\) 个村庄是 WJJ 打算去的地方。这些村庄之间有 \(M\) 条双向道路连接着,第 \(i\) 条双向道路恰好直接连接两个小村庄 \(A_i\),\(B_i\),长度为 \(C_i\)。道路有的是隧道,有的是栈桥,地图上那些看起来在村庄之外交叉的路实际上并不相交——也就是说,如果把这些小村落和双向道路构成的道路网看作图论意义上的图,我们不保证它是平面图,也不保证它没有重边。不过,有一点还是可以保证的:WJJ 细心地验证过,从它居住的村落一定能走到她想去的那个山谷。在 WJJ 所在的神奇世界中,每只小动物都可以借助仙人掌来施放魔法,其中之一是,交换世界中任意两条双向道路的长度,同时保持其他道路的长度不变。按 WJJ 目前的魔法水平,她最多能使用 \(K\) 次这种道路长度交换魔法。可惜的是,由于仙人掌刺比较多,WJJ 并不打算带着它旅行,于是她会在家里完成想要的道路交换后再出门。假设 WJJ 的旅行途中不会有其他小动物进行道路交换来破坏她设计好的路线。为了尽快达到目的地,WJJ 希望她需要走的总距离越短越好。也就是说,使用最多 \(K\) 次魔法后,从村落 \(1\) 到村落 \(N\) 的最短距离是多少?\(1\leq N\leq 50\),\(1\leq M\leq 150\),\(1\leq K\leq 20\),\(1\leq A_i,B_i\leq N\),\(A_i\neq B_i\),\(1\leq C_i\leq 1000\)。
这道题第一眼的想法应该是考虑用最短路的形式 dp,但你发现状态难以设计,考虑如果是一棵树的话那 \(1\) 到 \(n\) 的路径是一定的,调就完了,考虑如何确定这样的一条路径,但是你发现你没法枚举路径,似了。然后题解区告诉你可以枚举路径上出现了前 \(k\) 小的边,然后设 \(dp(u,i,j)\) 表示走到 \(u\),路径上有 \(i\) 条本来就在前 \(k\) 小的边,用了 \(j\) 次魔法的最短路,但关键问题是我枚举 \(k\) 的动机是什么。你发现对于一条路径调的方式肯定是换进来小的一些边,换出去大的一些边,但是具体换出去还是换进来这并不好说,因为没法枚举路径,我们尝试用这个 \(k\) 去反推路径,发现在确定了 \(k\) 之后路径就可以用最短路状的 dp 确定了。换句话说,让我们枚举 \(k\) 的动机是确定路径的需求与无法枚举路径之间的矛盾。
请忽略上面关于动机的瞎扯。经过思考,本来图上最短路状 dp 就是把路径上的有用信息压缩到状态里面,而让我不理解这道题动机的,或者说让我对 dp 产生误解的是数据范围,拿到这个题的第一眼我想的是直接通过状压枚举路径,很唐。关于这道题枚举 \(k\) 的动机,我认为应该是对确定了路径的情况下怎么调整答案的思考,你发现调整答案肯定是要选一个分界点,让比其小的调进来,比其大的调出去。
[CTS2019] 氪金手游
小刘同学是一个喜欢氪金手游的男孩子。他最近迷上了一个新游戏,游戏的内容就是不断地抽卡。现在已知:
- 卡池里总共有 \(N\) 种卡,第 \(i\) 种卡有一个权值 \(W_i\),小刘同学不知道 \(W_i\) 具体的值是什么。但是他通过和网友交流,他了解到 \(W_i\) 服从一个分布。
- 具体地,对每个 \(i\),小刘了解到三个参数 \(p_{i,1},p_{i,2},p_{i,3}\),\(W_i\) 将会以 \(p_{i,j}\) 的概率取值为 \(j\),保证 \(p_{i,1}+p_{i,2}+p_{i,3}=1\)。
小刘开始玩游戏了,他每次会氪一元钱来抽一张卡,其中抽到卡 \(i\) 的概率为:\(\frac{W_i}{\sum_j W_j}\)。小刘会不停地抽卡,直到他手里集齐了全部 \(N\) 种卡。
抽卡结束之后,服务器记录下来了小刘第一次得到每张卡的时间 \(T_i\)。游戏公司在这里设置了一个彩蛋:公司准备了 \(N-1\) 个二元组 \((u_i,v_i)\),如果对任意的 \(i\),成立 \(T_{u_i}<T_{v_i}\),那么游戏公司就会认为小刘是极其幸运的,从而送给他一个橱柜的手办作为幸运大奖。游戏公司为了降低获奖概率,它准备的这些 \((u_i,v_i)\) 满足这样一个性质:对于任意的 \(\varnothing\ne S\subsetneq\{1,2,\ldots,N\}\),总能找到 \((u_i,v_i)\) 满足:\(u_i\in S,v_i\notin S\) 或者 \(u_i\notin S,v_i\in S\)。请你求出小刘同学能够得到幸运大奖的概率,可以保证结果是一个有理数,请输出它对 \(998244353\) 取模的结果。
对 \((u_i,v_i)\) 的限制相当于告诉你这样的关系构成了一颗树,边有向,发现结构与 SAO 很一样,所以考虑先求出外向树的情况再对于反边容斥。然后我把题读错了,认为只要满足一个就行,然后想复杂了。考虑对于 \(u\),它是否合法只跟子树有关系,假设子树内的 \(W\) 的和是 \(s\),所有点的 \(W\) 的和是 \(S\),其子树内的时间都比他小的概率就是 \(\frac{W_i}{S}\sum_{i=1}^n(\frac{S-s}{S})^i\),直接 \(dp(u,s)\) 维护 \(u\) 和 \(s\) 然后转移就行了。然后对于反向边,我们钦定一些反向的边是正过来的,然后把其他边扔掉,对于外向树森林 dp 就是对的,时间复杂度 \(O(2^nn^2)\),考虑优化枚举的过程,发现在 dp 的时候 \(u\) 的情况只和 \(W\) 的和有关系所以 \(dp(u,s,k)\) 在原来的 dp 的基础上反了 \(k\) 条反向边的答案,然后和 SAO 一样这里的 \(k\) 可以直接带着容斥系数扔到答案里,这样就 \(O(n^2)\) 了。

浙公网安备 33010602011771号