阶乘取模

Published: by Creative Commons Licence

  • Tags:

阶乘取模

给定素数p,以及某个数0n<p,要求计算n!,其中p109+7

讲两种做法,第一种时间复杂度为O(n(log2n)2),第二种时间复杂度为O(nlog2n)

我们记fd(x)=di=1x+i。我们记m=n,那么我们只需要计算出fm(0),fm(m),,fm((m1)m),并将它们乘起来,缺失的部分最多O(m)项,我们暴力处理即可。

那么怎么计算fm(km)呢,其中0k<m。我们可以发现fm是一个m阶的多项式,我们可以先求出通过分治+FFT以O(m(log2m)2)时间复杂度快速求出多项式,之后利用多点求值的技术O(m(log2m)2)的时间复杂度内求出这m个值。

但是众所周知,这个多点求值的算法常数是真的大,假如你真的用这个方法,那很有可能会TLE的。下面我们提第二种方法。

我们考虑倍增的方法,假设我们已知:fd(ks),其中0kd。下面我们考虑怎么计算fd+1(ks)以及f2d(ks)

可以发现fd+1(ks)=fd(ks)(ks+d+1),而对于fd+1((d+1)s),我们可以直接暴力计算,时间复杂度最多O(n)

而对于f2d(ks),我们可以通过f2d(ks)=fd(ks)fd(ks+d)求解。右边项我们需要的是

  • fd(ks),0k2d
  • fd(ks+d),0k2d

g(x)=fd(xs),可以发现问题实际上是给定了g(0),,g(d),要求插值出g(k+δ),其中0kd。考虑通过拉格朗日插值法求解g

g(x)=di=0g(i)dj=0,jixjij

由于我们仅利用上面公式求解未知的量,因此一定有xi。我们可以做下面变换:

=di=0g(i)dj=0xj(xi)jiij=[dj=0xj][di=01(xi)g(i)dj=0,jiij]

下面我们考虑如何计算g(t),其中左边的项可以通过O(d)时间复杂度预处理阶乘,之后每个请求O(1)处理。而对于右边的部分,可以发现它可以描述为两个多项式的卷积,我们提前通过FFT处理出卷积的结果即可,预处理的时间复杂度为O(dlog2d),回答单个请求的时间复杂度为O(1)

这里提一下上面出现了比较多的xi,我们可以保证它不可能是0。因为我们可以保证所有要求的g(k+δ)都是未知量,即和0,1,,d无交集。

上面的过程总的时间复杂度为O(1log21+2log22++mlog2m)=O(mlog2m)=O(nlog2n)

参考资料