在进行一些序列计数问题时,会遇到状态转移的时候限制 ai 与相邻两个数 ai−1,ai+1 的关系(如大小、差值等)。为了更好地解决此类问题,可以在序列两端插入新的值(一般按照大小关系有序插入),所以计数的策略是钦定一些固定的段中间不能再插入新的值了,每次插入只能在两个固定的段中间的区域插入,所以叫做连续段 dp。
P5999
考虑把跳跃的排列拿出来,这个排列需要满足的要求如下:
考虑从小到大插入数值,钦定一个连续段内都是合法的,且连续段的开头大于它下一个数,连续段的结尾小于它上一个数(首尾除外)。设 dpi,j 为插入了 1∼i,且连续段个数为 j 的方案数,设 di=[i>s]+[i>t]。
考虑转移,特殊处理 i=s,i=t,有 dpi,j=dpi−1,j−1+dpi−1,j,表示是否在开头/结尾新建一段。当 i≠s∧i≠t 时,有三种转移。
-
新建一个段,dpi,j=dpi−1,j−1×(j−di)。
-
拓展一个段,在本题中,一个合法的连续段无法进行拓展,因为拓展之后这个连续段并不满足第一个/最后一个大于后一个/前一个数了。
-
合并两个段,dpi,j=dpi−1,j+1×j。
复杂度为 Θ(n2)。
CF1515E
可以按照时间顺序依次加入电脑,即先加入第一次开的,再加入第二次开的,在加入的时候考虑是否有 i−1,i+1 都开了的情况。
相同地,考虑设 dpi,j 为加入了 i 个电脑,有 j 个加入时间的连续段的方案数。由于没有头和尾的限制,所以不用特殊处理,转移如下。
-
新建一个段,dpi,j=dpi−1,j−1×j
-
拓展一个段,可以一次拓展一个或两个,dpi,j=2j×(dpi−1,j+dpi−2,j)
-
合并两个段,可以一次加入两个或三个到中间,不能只加一个,dpi,j=2j×dpi−2,j+1+j×dpi−3,j+1
答案即为 dpn,1,复杂度 Θ(n2)。
P7967
显然题目可以转化为:对于每个 k 求排列 p 的个数满足 n−1∑i=1max(api,api+1)=k,贡献是 (l−kn)。
将 ai 排序, 考虑设 dpi,j,k 为加入了前 i 小的数,连续段个数为 j,且每个连续段内部的 max(api,api+1) 之和为 k 的方案数。转移和上面类似。
dpi,j,k←dpi−1,j−1,k×j,dpi,j,k←2j×dpi−1,j,k−ai,dpi,j,k←dpi−1,j+1,k−2ai×j,分别表示新建、拓展、合并段。
最后答案即为 l∑i=0dpn,1,i×(l−in)。
复杂度 Θ(n2l)。
P9197
考虑从小到大加入 ai,那么可以把绝对值拆开,考虑每个数的贡献。对于一个 fi,若加入 i 时 i 左边、右边都没有数,贡献为 −2fi,若有且仅有一边有数,贡献是 0,否则是 2fi。
那么可以设计 dpi,j,k,d 表示加入 a1∼ai 有 j 个连续段,当前所有数的贡献之和为 k ,开头结尾钦定的个数 d(d∈{0,1,2})的方案数。转移有:
-
dpi,j,k,d=dpi−1,j−1,k+2fi,d×(j−d)+dpi−1,j−1,k+fi×(3−d)×[d=1∨d=2]
-
dpi,j,k,d=dpi−1,j,k,d×(2j−d)+dpi−1,j,k−fi×(3−d)×[d=1∨d=2]
-
dpi,j,k,d=dpi−1,j+1,k−2fi,d×j
可以发现,由于有正负性的存在,可能 k 会先减到很小,后面再加回来。所以第三维实际上是 Θ(nl) 的,总复杂度是 Θ(n3l) 的,无法通过。
考虑有没有什么办法能让 k 单调不降。考虑差分,设 bi=ai+1−ai,钦定 bn=0,那么一个 bi 的贡献为满足 min(pj,pj+1)≤i,max(pj,pj+1)>i 的 j 的个数。
那么当加入 i 时,已经加入的所有数(包括 i)旁边没加入的数的个数之和就是贡献的个数。dp 状态相同,转移如下。
-
dpi,j,k,d=dpi−1,j−1,k−bi(2j−d),d×(j−d)+dpi−1,j−1,k−bi(2j−d),d−1×(3−d)×[d=1∨d=2]
-
dpi,j,k,d=dpi−1,j,k−bi(2j−d),d×(2j−d)+dpi−1,j,k−bi(2j−d),d−1×(3−d)×[d=1∨d=2]
-
dpi,j,k,d=dpi−1,j+1,k−bi(2j−d),d×j
答案为 l∑i=0dpn,1,i,2,复杂度 Θ(n2l)。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步