题目传送门:
https://www.luogu.org/problemnew/show/P4245
题目分析:一道任意模数多项式乘法的模板题。可以写拆项+FFT,或者三模数NTT。我暂时只写了后者。
具体做法是这样:先选取三个乘积在
1023" role="presentation" style="position: relative;">1023以上的便于使用NTT的模数。在这里我选的是
m1=998244353=223∗119+1" role="presentation" style="position: relative;">m1=998244353=223∗119+1,
m2=1004535809=221∗479+1" role="presentation" style="position: relative;">m2=1004535809=221∗479+1,
m3=469762049=226∗7+1" role="presentation" style="position: relative;">m3=469762049=226∗7+1。选这三个模数的好处在于它们的原根都是
3" role="presentation" style="position: relative;">3。
然后用这三个模数做NTT,可以得到以下三条式子:
ans≡c1(modm1)" role="presentation" style="text-align: center; position: relative;">ans≡c1(modm1)
ans≡c2(modm2)" role="presentation" style="text-align: center; position: relative;">ans≡c2(modm2)
ans≡c3(modm3)" role="presentation" style="text-align: center; position: relative;">ans≡c3(modm3)
虽然这三条式子可以在
1023" role="presentation" style="position: relative;">1023以内唯一固定
ans" role="presentation" style="position: relative;">ans的值,但问题也随之而来:
m1∗m2∗m3" role="presentation" style="position: relative;">m1∗m2∗m3很大,无法直接用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后相乘。
为了方便,把上式化成这样的形式:
ans≡C(modM)" role="presentation" style="text-align: center; position: relative;">ans≡C(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;">x在
modm3" role="presentation" style="position: relative;">modm3意义下的值: