快速阶乘算法(暂无实践)

Problem

模板题luogu5282

  • n! mod pn! mod ppp是质数
  • 由于是任意模数,所以需要MTT。

nlog2nsqrt{n}log^2n

  • 一种暴力 的方法是多项式加分块,设定一个块的大小BB,以及这样一个多项式:
    f(x)=i=1B(x+i)f(x)=prod_{i=1}^B{(x+i)}
  • 那么答案就是f(0)f(B)f(2B)...f(n/BB)Bf(0)*f(B)*f(2B)*...*f(n/B*B)*最后剩下不超过B的部分的乘积
  • 直接利用多项式多点求值可以搞到nlog2nsqrt{n}log^2n的复杂度。

nlognsqrt{n}logn

  • 直接求系数实在是太慢了,我们可以考虑另一种多项式的优秀处理方法——点值。
  • fd(x)=i=1d(x+i)f_d(x)=prod_{i=1}^d(x+i),那么我们最后就是要求所有的fB(iB)f_B(iB)
  • 利用倍增的思路,如果我们可以做到从fd(0,B..dB)f_d(0,B..dB)f2d(0,B..2dB)f_{2d}(0,B..2dB)的乘二的转化,以及加一的转化,那么就可以倍增求出最后的BB个点值了

乘二

  • 对于fd(0,B...dB)f_d(0,B...dB),首先需要变成fd(0,B...2dB)f_d(0,B...2dB).
  • 然后再在对应位置上乘上fd(d,d+B...,d+2dB)f_d(d,d+B...,d+2dB),就可以变成f2d(0,B...2dB)f_{2d}(0,B...2dB)
  • 考虑第一步需要得到fd(dB...2dB)f_d(dB...2dB),再综合第二步,都相当于是从fd(iB)f_d(iB)变成fd(iB+x)f_d(iB+x)
  • 系统化得来说,假设h(i)=fd(iB)h(i)=f_d(iB),那么相当于我们已知h(0..d)h(0..d),要求h(0+x/B,1+x/B..d+x/B)h(0+x/B,1+x/B..d+x/B)
  • 运用拉格朗日插值:
    h(Δ+n)=i=0dh(i)jiΔ+njijh(Delta +n)=sum_{i=0}^dh(i)prod_{j eq i}frac{Delta+n-j}{i-j}
    =j=0d(Δ+nj)i=0dh(i)(1)nii!(ni)!1Δ+ni=prod_{j=0}^d(Delta+n-j)sum_{i=0}^dfrac{h(i)*(-1)^{n-i}}{i!(n-i)!}*frac{1}{Delta+n-i}
  • 由于d<=Bd<=B,所以不会出现Δ+nj=iDelta+n-j=i的情况。后面可以直接FFT,前面的只需要用双指针扫一遍即可。

加一

  • fd(0,B..dB)f_d(0,B..dB)fd+1(0,B..dB,(d+1)B)f_{d+1}(0,B..dB,(d+1)B)直接暴力即可。

时间复杂度

  • T(n)=T(n/2)+n log n=T(n log n)T(n)=T(n/2)+n log n=T(n log n)
原文地址:https://www.cnblogs.com/DeepThinking/p/13090886.html