题目
有一个长度为(n)的数列(a_i),一开始将数列的前(L)个丢入队列中。
一次操作是对于队列中的每个数(a_i),有(a_i)的几率有(1)的贡献。设贡献和为(x)。
然后将队列中前(x)个弹出去,再从数列中接着(x)个。
如果数列中的数取完了,操作停止。
问期望进行多少次操作。
思考历程
一开始就看错了题意,于是这就变成的了一道神仙题。
只想到了(a_i)相等的情况。
由于时间不够,写出来之后调都不调就交了。
结果自然是爆0。
(话说我在写这个东西的时候没有用分治NTT……不知道是我的方法错了,还是确实能不用分治NTT……)
正解
设(f_i)为当前队列中的数在([i,i+L))中的期望。
转移十分显然,这里就不写了。
直接暴力转移肯定不能过,接下来就是在这个东西的基础上优化。
把整个序列倒过来做,(f_i)就变成了当前队列中的数在((i-L,i))中的期望。
设(P_i=prod_{j=i-L+1}^i (1-a_j+a_jx))
随便推一推得:
接下来问题是处理(sum_{j=1}^Lf_{i-j}[x^j]P_i),其它的都很好办。
设(F_i=sum_{j=L}^if_jx^j)
于是(sum_{j=1}^Lf_{i-j}[x^j]P_i=[x^i](F_{i-1}P_i))
这下就好看很多了。
考虑分治NTT。假如现在要计算区间([l,r])的答案。
对于每个(iin[l,r]),我们把(P_i)的公因数提取出来,就是(prod_{j=r-L+1}^l(1-a_j+a_jx))
设(F_{l,r}^{'}=F_{l-1}prod_{j=r-L+1}^l(1-a_j+a_jx)),这就是前面([1,l-1])区间对每一个(iin [l,r])共有的贡献,可以一起算。
([x^i]F_{i,i}^{'})就是对(f_i)的贡献。
考虑如何从(F_{l,r}^{'})转移至(F_{l,mid}^{'})和(F_{mid+1,r}^{'})
上面式子中的那堆连乘都是可以用分治NTT预处理的。
看起来这个东西并没有优秀到哪里去啊……
我们想要求的,仅仅是([x^i]F_{i,i}^{'})
对于(F_{l,r}^{'}),如果它一直转移到了叶子节点,那么乘上的多项式的次数是(O(r-l))级别的。
我们最终只要对(iin [l,r])的所有(i)有用的项,这意味着有些次数很小的项,他们次数增高(O(r-l))之后依然不会对任何(iin[l,r])的(i)产生影响,这个项就是没用的。
对于没用的项,那就可以忽略不计。
所以,只需要存最后(O(r-l))项就可以了。题解说标程存了最后(2(r-l+1))项。
于是这题就做完了,时间复杂度(O(n lg^2 n))。
总结
一个晚上,两个小时思考,再加一个小时写博客整理思路。
这种到现在pty都没有AC的题目,我不知道写出来要多长时间。
就先把题解晾在这里好了……
这题的精髓,除了乱推一波式子以外,我认为是最后的那个保留最后几项的那个操作。
我终于明白了原来分治NTT还能这么搞。