-
给每个对象决策属性,贡献与每个对象的属性相关。
-
如果直接 DP 决策每个对象具体的属性,难以找到一个顺序,使得前面决策的属性不会再影响贡献。
-
有一个单调的简单标准,把所有决策分作两部分:一部分是确定不会再影响贡献的(确定部分)、一部分是有可能会再影响贡献的(待定部分)。已经进入确定部分的决策不会再回到待定部分。
-
确定部分等价。待定部分等价。
-
此时可以考虑记录这个标准,下一个对象的决策分为 "让它的属性作为确定部分" 或 "让它的属性作为待定部分"。同时在标准发生改变的时候再处理那些从待定部分进入确定部分的决策。
一个经典的贡献就是 mex,按什么顺序决策都有可能出现先
1 后
0 导致 mex 直接变到
2。但是 mex 随着出现的数的集合扩大,显然是单调不降的。而且若决策一个数
<mex 是不会影响 mex 的。
例一:P7213
先考虑按什么顺序。显然要么按值域来看,要么按顺序来看。但是如果按值域来看需要同时处理两个位置,所以我们考虑按顺序。那是从前往后还是从后往前呢?注意前面的数是否会减少,要依赖后面的数,所以我们应该从后往前、这样记录的信息就可以判定当前数要怎么减少。
然后来考察性质。首先如果后面出现了一个数
x,那么前面的数不可能等于
x。进一步观察,我们发现操作其实可以不看作是每一轮都对整体操作,而是转为:从后往前扫,只要当前的数后面出现过就
−1,直到变成
0 或者后面没有。扫完了就得到最终的结果。
所以一个数
x 要变成
0,等价于后面
1∼x 都出现了。那么我们可以大致得出一个数如果填
x 就有两种可能:
-
若后面
1∼x 都出现了,变成
0。
-
若后面
1∼x 里有漏的,变成漏的里最大的。
那么难点就在于我们怎么判断
1∼x 里有没有漏的。
如果具体决策每个数等于几,那就难以避免状态每个数是否出现。但是此时发现有一个单调性:如果已经连续出现了
1∼x,那么未来连续出现的上限只会比
x 大。所以
决策一个数为 ≤x 不会改变这个连续高度,而决策 >x 有可能改变。
把当前后缀里的数分为两个部分:
1∼h、
≥h+2 的数。
考虑在 DP 过程中记录连续出现的高度,决策下一个数是
≤h 还是
≥h+2,前者立刻算贡献,后者待定。当
h 发生变化时,决策待定的元素里有哪些要被确定。
设
dp(i,h) 表示
i∼n 的高度决策好了,最大连续出现高度为
1∼h 的方案数。
此时我们应该要预见到(或者在下面推的时候碰到):一种高度只有两个名额,当剩下两个名额或者一个名额,可以做
1 的贡献;如果不剩下名额,就做
0 的贡献。而从两个名额变成一个名额、和从一个名额变成没有名额,这两种变化是本质不同的!所以难道我们要额外记录有多少个高度是两个名额的吗?
这样做复杂度肯定爆炸了。考虑上面遇到的问题,我们可以认为相等的两个数也是有区分的,最终给答案除以
2n。这样有两个名额的贡献系数就是
2,从而转为有多少个名额可选,贡献系数就是多少。
然后考虑转移到
i−1。设
i∼n 里有
pi 个要变成
0,
qi=n−i+1−pi 个要留下。
-
若
i−1 要变成
0,则
i−1 的高度要
≤h。
一共有
2h 个名额,但是有
h 个作为
1∼h 的连续前缀,以及
pi 个变成
0 的初始高度肯定
≤h,因此被占用了
h+pi 个名额,剩下
h−pi 个。
所以
dp(i,h)×(h−pi)→dp(i−1,h)。
-
-
i−1 最终高度
>h+1。
那不会更新
h,先放着在以后考虑。
dp(i,h)→dp(i−1,h)。
-
i−1 最终高度
=h+1。
考虑枚举加入
i−1 后新的连续出现高度
h′。那么要在后面的自由高度里选
h′−(h+1) 个来填补空缺。
所以后面的数又分成两部分:
≤h′ 的、
>h′+1 的。
考虑贡献系数。
-
先选出哪些位置要来补空缺,方案数
(h′−(h+1)qi−h)。
-
一共
2(h′−h) 个名额,后面要用
h′−(h+1) 个名额,还有
h′−h+1 个名额。
-
最后还要考虑后面的位置怎么选使得能刚好消到
h+2∼h′。
显然这个问题等价于
1,1,2,2,…,h′−h−1,h′−h−1 里选
h′−h−1 个数出来,操作完了刚好是
1∼h′−h−1 的排列的方案数。
但是这个东西不是很好直接求,所以设辅助数组
gn 表示 "在
1,1,2,2,…,n,n 里选
n 个数,操作完了剩下
1∼n 的排列的方案数"。
我们考虑极端值原理,枚举最终
1 号位的高度
x,那么其原本高度在
[x,n] 里有
2(n−x+1) 个名额,而之前用了
n−x 个在
[x+1,n] 里,所以还剩
n−x+2 个名额。
同时我们发现后面最终高度为
1∼x−1 和
x+1∼n 的不会相互干扰了(因为没有
x 供给它们跨越中间),所以有转移:
gn=x∑(n−x+2)(x−1n−1)gx−1gn−x
方案数
gh′−h−1。
所以
dp(i,h)×(h′−(h+1)qi−h)(h′−h+1)gh′−h−1→dp(i−1,h′)。
转移即可,复杂度
O(n3)。不要忘记最终除以
2n。
在本题中,单调的标准即 "连续出现高度
h"。如果决策
≤h,不会改变
h;而决策
≥h+2 可能在后面改变
h,也可能不会影响。于是待定后者的决策即可。
例二:P14364
把计数排列看作天和人的匹配。按值域考虑每个人和哪个匹配。大致为
dp(i,j) 表示
c≤i 的人里有
j 个面试失败的方案数。
然后就发现当决策一个人面试失败的时候并不只涉及
c 比它小的人,因为可以让一个
c 大的人遇到
0,所以还要额外记录后面有多少个人面试失败了。
为了避免记录每个人的状态,我们要考虑这个单调的标准,从而把同属于 "确定" 或 "待定" 的人能同类看待。
把
(i,cpi) 在坐标系上画成柱状图,画一条左下到右上的折线表示每一天的失败人数变化。这样就容易看出因为折线是单调向上的,所以在某次折线达到
x 的高度后,之后耐心值
≤x 的人都不可能被录取,而
>x 的人只要放在
1 的位置就一定会被录取。
这就是单调的标准。
所以考虑把人分作耐心值
≤x,>x 考虑。因为
x 是单调上升的,所以过程中会有一些
>x 的人进入
≤x,但是
≤x 的永远会
≤x。所以 "
≤x 的人" 是确定的部分,"
>x 的人" 是不确定的部分。考虑设
dp(i,j,k) 表示决策前
i 天的安排、有
j 个人失败、有
k 个位置的人耐心值
>j(等待后面决策),在
dp 里只决策前
i 天耐心值
≤j 的方案数。
使用刷表法转移。考虑第
i+1 天的人是要失败还是要通过。记
cnti 表示耐心值为
i 的人数,
sc 为
cnt 的前缀和。
-
必须失败。分类讨论
cpi+1 与
j+1 的大小关系,并把
k 个人里
=j+1 的纳入考虑。
-
cpi+1>j+1。
w=0∑k(wk)(wcntj+1)w!dp(i,j,k)→dp(i+1,j+1,k−w+1)
-
cpi+1≤j+1。
w=0∑k(wk)(wcntj+1)w!(scj+1−(i−(k−w))dp(i,j,k)→dp(i+1,j+1,k−w)
-
-
cpi+1>j,此时会录取,
j 不变。
dp(i,j,k)→dp(i+1,j,k+1)
-
cpi+1≤j,此时会放弃,
j 加一。
w=0∑k(wk)(wcntj+1)w!(scj+1−(i−k))dp(i,j,k)→dp(i+1,j+1,k−w)
虽然看起来要枚举
w 是
O(n4) 的,但因为总人数
O(n),所以
w 一共只能枚举
n 次。故总复杂度是
O(n3) 的。
例三:CF1608F
考虑转换计数对象,令
si 为
a 的前缀
mex 数组,对
s 计数再考察一个
s 可以对应多少个
a。
对于
s,显然应该有
si≤i 和
si≤si+1 和
si∈[bi−k,bi+k]。
考虑固定
s 后
a 有多少种选取方案。手玩一下,首先能发现一个简单的性质:
si=si+1 时,
ai+1=si。于是这启示我们把
s 分成若干个相等段来考虑。
于是设划分的结果是
[l1=1,r1],[l2=r1+1,r2],…,[lk,rk=n]。每段内的
s 都相等。那么有:
-
对
2≤i≤k 有
ali=sri−1。
-
对
1≤j≤ri 有
aj=sri。
-
为了使
ali 一加入,就从
sri−1 增加到
sli,则
a1∼ari−1 必须包含
(sri−1,sli) 的所有数。
-
如果
s1=1 则
a1=0。
感受一下,以上的条件已经充分了。所以开始 DP:因为有
mex 运算,所以采用延迟决策、记录待定位置数的方法,
f(i,j,k) 表示前
i 个位置,
si=bi+j,有
k 个位置上的
a 是没确定的(
>si 的个数),方案数。
考虑转移。枚举下一位
si+1=bi+1+p(
p∈[−k,k]):
-
si=si+1,则
f(i,j,k)→f(i+1,p,k+1)。
-
si<si+1。有
ai+1=si,还要从前面待定的
k 个位置里选
k′ 个,赋值到
(si,si+1) 里,且每个数都要取到,这里面是一个要推的系数。
就算假设那个系数可以
O(1) 求,状态是
O(n2k) 的,枚举
si+1 是
O(k) 的,若
si<si+1 还要枚举放多少个下来还是
O(k) 的。复杂度是
O(n2k3) 起步,肯定过不了,必须优化。
接下来是一个很有道理但是很巧妙的想法:
mex 只关心每种数是否出现过,那如果我们待定的方式是有
k 种数是 >si 的,就非常方便了。所以我们改设
f(i,j,k) 表示前
i 个
si=j 有
k 种数待定。
那么
si=si+1 时,
ai+1 可以选择新开一种或者选择等于旧的一种。而
si<si+1 时只需要从
k 种里选
si+1−si−1 种就行了!
所以更改了一下定义,轻松就做到了
O(n2k2)。但是这个复杂度还是过不了。继续优化。写出转移式子:
f(i,j,k)→f(i+1,j,k+1)
f(i,j,k)⋅(j+k)→f(i+1,j,k)
f(i,j,k)⋅((bi+1+j′)−(bi+j)−1k)→f(i+1,j′,k−[(bi+1+j′)−(bi+j)−1])
现在来优化。前两条不用优化,看看第三条是什么东西,发现枚举
j′ 完全不如直接枚举
bi+1+j′−(bi+j)−1。重写一下就是
f(i,j,k) 会转移到
f(i+1,j+c,k−c) 这条斜线上。用个前缀和优化即可。
upd:有一个聪明的方法可以不用前缀和优化。我们不用 "一次性" 决定好
mex 要增长到哪里。可以认为
mex 有两种增长方式:在
ai 放一个
mex,或者在目前待定里取一种放
mex。所以可以写成
f(i,j,k)←f(i−1,j−1,k)+f(i,j−1,k+1)×(k+1)。
例四:图灵杯第七届高级组T1 棋无常树
令
mxbi 为
i 子树内
bi 的最大值,则赋值后
i 子树内
0∼mxbi−1 都应该出现过。同时
i 子树内
a=−1 的数也确定出现过。定义
visi,j=0/1 表示
i 子树内数
j 是否出现过。
定义
fi,j,k=0/1 表示在
i 子树内填
a,使得 "
=mxbi 且不确定是否出现过的数里" 有
j 种不同的数,且
mxbi 没有出现/出现了 的方案数。所以
这个状态实际上把数分成了三类:确定的数、不确定的数、mxbx,
在转移时就要让这三类数指代的对象统一了才能转移。
考虑合并
x 和其子树
i,为了方便起见,不妨
mxbx≥mxbi。记
szu 为当前
u 子树内有多少个
a=−1 的,
也就是 f 数组的第二维在有意义的前提下最高取到多少。
因为
f 的定义是基于
vis 的,第一个任务是合并
fx,fi 的
vis。
当我们发现
visx,p=1,visi,p=0 时,我们要把
visi,p 改成
1,那这会对
fi 有什么影响?
p 本来在
i 的视角下属于 "不确定是否出现过的数",现在变成确定出现的了。所以:
fi,j,k←fi,j,k+fi,j+1,k⋅j
也就是从
j+1 个不确定的里面选一个
p 出来变成确定的,这样更新一次是
O(szi) 的。
visx,p=0,visi,p=1 的同理对
fx 更新,一次
O(szx)。
这个操作会经常出现,我们称它为 trans。可以发现,当有一个新的确定数出现,为了确保不确定数和确定数之间没有重复,就需要做一次 trans。
然后
visx,visi 相等了,开始合并
fx,fi。为了方便,搞一个
gj,0/1 作为临时数组,
fx∗fi 先放到
g 里面,转移完了再从
g 复制回
fx。
-
先考虑
mxbx=mxbi 的情况。一个 naive 的想法是
fx,j,a⋅fi,k,b→gj+k,max(a,b)。但这个转移方程是不对的,原因是
x 里的
j 种不同数可能与
i 里的
k 种不同数有重合,导致最终的不同种类数比
j+k 少。
枚举
j:0→szx。我们希望
fx,j,a⋅fi,k,b 可以直接转移,问题在于两棵子树内的不同数会有重合。所以
一个想法是随着 j 的变动,始终保持 fi,k 的 k 个不同数里与 fx,j 的 j 个数不重合。
那么当
j 增大一,我们只需要处理这
k 个不同数里出现了
x 的第
j 种数的情况。怎么处理?这和上面计算 "把
visi,p 改成
1,那这会对
fi 有什么影响" 是完全一样的。于是只需要 trans 一遍即可。
-
那么只剩下
mxbx>mxbi(开头的时候有不妨
mxbx≥mxbi)。
容易发现上面
j 增大一就对
fi 做一次 trans 的想法是可以沿用的。但是现在又增加了一个新的问题:
fi,k,0/1 的第三维
0/1 指的是
mxbi 而非
mxbx。要想让
fi 转移到
x 的子树内,又要分类讨论
fi 里有没有
mxbx。
-
如果
visi,mxbx=1,那么显然第三维只能取
1。也就是
fx,j,a⋅fi,k,b→gj+k,1。
-
否则,如果
fi 里没有
mxbi:转移方程
fx,j,a⋅fi,k,b→gj+k,a。
如果
fi 里有
mxbi:转移方程
fx,j,a⋅(k⋅fi,k,b)→gj+k−1,1。
我们终于合并了所有子树,太好了!但是现在还需要把
x 的影响加入
fx 内。这又要根据
ax,bx 分类讨论。令
s 表示
x 子树内有多少种确定的数会出现(
s=∑ivisx,i)。
-
ax=bx=−1。
-
赋值成一个新的不确定数。
fx,j+1,a←fx,j+1,a+fx,j,a。
-
赋值成一个确定数或者一个旧的不确定数。
fx,j,a←fx,j,a×(s+j)。
-
赋值成
mxbx。
fx,j,1←fx,j,1+fx,j,k。
-
ax=−1,bx=−1。
此时无法赋值成不确定数,只能是确定数或者
mxbx。
-
ax=mxbx,那么
fx 的第三维只能是
1(必然存在
mxbx)。令
fx,j,1←fx,j,1+fx,j,0 然后
fx,j,0←0。
-
若在
x 之前已经有
ax 了,那没有任何影响。
-
否则
x 是一个新出现的确定数,也就是
visx,ax←1。为了排除
ax 与不确定数的重复,做一次 trans。
-
若
mxbx>bx 或
ax=bx 或
visx,bx=1,那么无解,直接退出。
-
bx=mxbx。
这种情况我们只接受
fx,?,0 的转移。因为如果第三维是
1,
x 的子树里就出现
bx 了,无解。所以一开始就让所有
fx,j,1←0。
-
ax=−1。可以赋值成确定数或旧的不确定数或新的不确定数。如果赋值成新的不确定数,
fx,j+1,0←fx,j+1,0+fx,j,0;如果赋值成旧的不确定数或确定数,
fx,j,0←fx,j,0⋅(s+j)。
-
ax=−1。若
ax 以前出现过,不用管;否则
visx,ax←1,然后为了排除
j 个不确定数里可能有
ax 出现,再做一次 trans 即可。
-
bx>mxbx。
这种情况下,最终的
mxbx=bx,所以最终的
fx 应该满足所有第三维是
1 的位置都是
0。
同时当
mxbx=−1,因为
bx 要满足,
[0,bx−1] 都出现过,所以
mxbx 也必须出现过。于是只能从原本第三维是
1 的
fx 转移。
因此要特别注意,现在的
f 第三维指的是
mxbx,需要的
f 第三维指向
bx。
-
mxbx=−1。注意这种情况显然应该有
fx 所有第三维是
1 的位置都是
0。
-
ax=−1。可以赋值成新的不确定数或旧的不确定数或确定数。
-
ax=−1。若
ax 之前出现过,不用理;否则
visx,ax←1 然后做一次 trans。
-
mxbx=−1。
-
如果赋值为一个新的不确定数,就是
fx,j+1,0←fx,j+1,0+fx,j,1。注意这里只能从
fx,j,1 出发转移。
否则不确定数个数(第二维)不增加,也就是转成旧不确定数或确定数或
mxbx。如果转成旧不确定数,贡献
j⋅fx,j,1;如果转成确定数,贡献
s⋅fx,j,1;如果转成
mxbx,因为
ax 处有了
mxbx,其他地方就可以使用
fx,j,0 转移了,贡献
fx,j,0+fx,j,1。
因此
fx,j,0←fx,j,0+(s+j+1)⋅fx,j,1。
然后
fx,j,1=0。
-
这里又要分为
ax=mxbx 和
ax=mxbx。
若
ax=mxbx,那么其他地方有没有
mxbx 都可以,
fx,j,0←fx,j,0+fx,j,1,然后
fx,j,1←0。
若
ax=mxbx,那么其他地方必须有
mxbx,所以
fx,j,0←fx,j,1,然后
fx,j,1←0。同时注意如果
ax 是新的确定数出现,做一次 trans。
最后,因为
bx 要满足,还要让一些不确定数变成
[mxbx+1,bx−1] 里的数。具体而言就是
fx,i−1,j←fx,i,j⋅i,然后让
szx 减一。不要忘了让
mxbx←bx。
于是 DP 的部分结束了,怎么计算答案?
令
s=n+1−∑ivis1,i−(mxb1=−1),那么
s 就是不确定数的选择范围。
ans←ans+(is)i!⋅f1,i,0/1
然后是复杂度分析。
-
合并
vis 数组的复杂度。把 trans 的
O(szi) 看作对子树内每个结点增加
1 的势能。对于结点
x 不断向上合并,每增加
1 的势能就表示
vis 出现了一个新的位置变成
1,而一共有
n+1 个位置可以变成
1,所以一个点贡献的总复杂度是
O(n)。于是这一部分总共贡献
O(n2)。
-
合并
f 数组的复杂度。把
O(szx⋅szi) 看作在两棵子树里各选择一个点,在 LCA 处贡献
1 的复杂度。容易分析出
O(n2)。
-
加入
x 的复杂度。容易做到
O(szx) 的复杂度。总共
O(n2)。