這題希望你的時間複雜度是O(Q(logQ)2+KlogK)。經過實測,確定在ZJ上O(QK)的naïve演算法沒辦法在30秒內跑完。
不難發現令f(x)=c0+c1x+…+cKxK,則題目問的就是f(a1),f(a2),…,f(aQ)除以M的餘數。假定你知道怎麼把兩個O(d)次多項式間的乘法和除法做到時間複雜度O(dlogd)。我們想算f(ai),一個方法是計算f(x)除以x−ai的餘式,但不幸的是直接計算f(x)除以每個x−ai的餘式還是O(QK)的。以下我們介紹一個使用divide and conquer降低時間複雜度的做法。
在[1,Q]區間建立一棵線段樹,其中負責[l,r]區間的節點儲存多項式p(x;l,r):=(x−al)(x−al+1)…(x−ar)。可以發現,建好這棵線段樹的時間複雜度是T(Q)=2T(Q2)+O(QlogQ)=O(Q(logQ)2)。接著,令l<r並設m=⌊l+r2⌋。假定我們想要計算某個多項式g(x)除以x−al,x−al+1,…,x−ar的餘式們,我們可以先計算g(x)除以p(x;l,r)的餘式h(x),再分別計算「h(x)除以x−al,x−al−1,…,x−am的餘式們」和「h(x)除以x−am+1,x−am+2,…,x−ar的餘式們」以得到答案。在根節點的取餘運算要花O(KlogK)時間。另一方面,對於一個負責區間長度為L的節點,它傳下去的多項式次數必定不超過L−1,故剩下的節點的取餘運算時間總和為2T(Q2)=4T(Q4)+O(QlogQ)=O(Q(logQ)2)。這樣一來,整體的時間複雜度就從O(QK)降低至O(Q(logQ)2+KlogK)了。
關於多項式的運算,有個地方要注意一下。這題的M可以到108,也就是QM2可能破unsigned long long的範圍。在寫乘法的時候,該對M取餘數的地方就不要偷懶。