洛谷P4245:【模板】MTT (CRT+三模数NTT)

2019-04-14 17:43发布

题目传送门:https://www.luogu.org/problemnew/show/P4245
题目分析:一道任意模数多项式乘法的模板题。可以写拆项+FFT,或者三模数NTT。我暂时只写了后者。 具体做法是这样:先选取三个乘积在1023" role="presentation" style="position: relative;">1023以上的便于使用NTT的模数。在这里我选的是m1=998244353=223119+1" role="presentation" style="position: relative;">m1=998244353=223119+1m2=1004535809=221479+1" role="presentation" style="position: relative;">m2=1004535809=221479+1m3=469762049=2267+1" role="presentation" style="position: relative;">m3=469762049=2267+1。选这三个模数的好处在于它们的原根都是3" role="presentation" style="position: relative;">3。 然后用这三个模数做NTT,可以得到以下三条式子: ansc1(modm1)" role="presentation" style="text-align: center; position: relative;">ansc1(modm1) ansc2(modm2)" role="presentation" style="text-align: center; position: relative;">ansc2(modm2) ansc3(modm3)" role="presentation" style="text-align: center; position: relative;">ansc3(modm3) 虽然这三条式子可以在1023" role="presentation" style="position: relative;">1023以内唯一固定ans" role="presentation" style="position: relative;">ans的值,但问题也随之而来:m1m2m3" role="presentation" style="position: relative;">m1m2m3很大,无法直接用long long存下,而用long double之类的则会丢失精度,所以无法用普通的CRT。难道要写高精度?不,有一种很妙的方法可以解决这个问题。 首先注意到这里只有三个模数,而且两个模数乘起来是不会爆long long的,所以可以先合并前两条式子。根据CRT,有: ans(c1m2Inv(m2,m1)+c2m1Inv(m1,m2))(modm1m2)" role="presentation" style="text-align: center; position: relative;">ans(c1m2Inv(m2,m1)+c2m1Inv(m1,m2))(modm1m2) 其中Inv(x,y)" role="presentation" style="position: relative;">Inv(x,y)表示x关于y的逆元。 这条式子涉及到两个很大的数相乘然后再取模,而直接相乘会爆long long。可以用O(log(m1m2))" role="presentation" style="position: relative;">O(log(m1m2))的快速乘,或者O(1)" role="presentation" style="position: relative;">O(1)转double后相乘。 为了方便,把上式化成这样的形式: ansC(modM)" role="presentation" style="text-align: center; position: relative;">ansC(modM) 然后设: ans=xM+C=ym3+c3" role="presentation" style="text-align: center; position: relative;">ans=xM+C=ym3+c3 接下来的部分才是精髓。我们求出x" role="presentation" style="position: relative;">xmodm3" role="presentation" style="position: relative;">modm3意义下的值: