看了下大佬们的题解,我不觉得这个把
01 看成不等号的等价转换是人类能想出来的东西,来一个我自己的思考过程吧。
本题还有一个思考方向:保留
?,去掉
1,通过容斥算贡献。
直接考虑题目中的过程,为了不算重,考虑加一个限制:每次删掉的数要满足
i=n∨si=si+1。
首先我们考虑一个基本的问题:对于一个长度为
n 的全问号串,求
fn。
我们此时需要确定
p 上的数是什么东西。现在我们翻开这个问号,发现
sp=1,则根据限制可以推得
sp+1=0;同理如果
sp=0,则
sp+1=1。
这一步相当于说,我们翻开了相邻的两个位置,并且删除了左边那个数。这里可以利用
01 的对称性重新用问号盖上右边那个数,这样就划归到了子问题
fn−1。
特别的,如果
p=n,则右边没有数,需要算两次贡献。
得到递推式
fn=(n+1)fn−1,所以
fn=(n+1)!。
接下来考虑有一些位置是
0 的情况。我们现在假设
0 在中间形成了一个长度为
l 的连续段,整个串长度为
n,设答案为
fn,l。
手玩以下有边界条件
fn,n=1,fn,0=(n+1)!。
枚举我们现在删掉了哪个位置。不同于之前的是,我们事先知道了一些位置上的值为
0,需要在这个情况下计算方案数。
- 如果 p=n 或 spsp+1=??,分析同上,fn−1,l→fn,l 贡献 n−l 次。
- 如果 spsp+1=?0,则这个问号下只能是 1,fn−1,l→fn,l。
- 如果 spsp+1=0?,则问号下的数是 1,可以用 1=?−0 容斥掉,fn−1,l−1−fn−1,l→fn,l。
整理一下,
fn,l=(n−l)fn−1,l+fn−1,l−1,得到
fn,l=(l+1)!(n+1)!。
归纳可得,如果存在多段连续的
0,则答案为
(n+1)! 除以所有连续段长度加一的阶乘。
我们现在将原字符串中的
1 拆成
?−0,设
dpi,l 表示当前考虑到为止
i,最后一段长度为
l 的答案的带容斥系数和。
- 如果 si=0,则 dpi,l−1×l+11→dpi+1,l。
- 如果 si=?,则 dpi,l→dpi+1,0。
- 如果 si=1,则 dpi,l−1×l+1−1→dpi+1,l,dpi,l→dpi+1,0。
事实上状态数少的可怜,在模拟赛里能骗 80pts。(骄傲)
会了
O(n2) 做法,剩下的就很简单了。
我们得先把这个 dp 式子压成一维的,枚举上一个被钦定为问号的位置
j,则
i,j 中间夹着的东西全是
0,贡献为
(−1)cnt(len+1)!1 ,可以拆成只和
i,j,j−i 有关的形式。使用分治 NTT 复杂度可以做到
O(nlog2n)。