[復習]多項式和生成函數相關內容
多項式
涉及的方面
主要在於多項式的乘法,也就是\(FFT,NTT,MTT\)。
但是也多項式的求逆,\(exp\),\(ln\),開根,求導,積分等操作。
多項式乘法
並沒有什么好復習的,記好板子就行了。同樣也是多項式運算的基礎。
泰勒展開&麥克勞林級數
泰勒展開:
如果\(f(x)\)在\(x0\)處存在\(n\)階導,那么就有:
其中\(\xi\)是余項,當\(n\)趨近於無窮大的時候,\(\xi\)趨近於高階無窮小。
當\(x0=0\)時是特殊情況下的泰勒展開,稱之為麥克勞林級數。此時\((x-x0)^i\)項變為了\(x^i\)。
最常見的就是\(e^x\)的展開
牛頓迭代
多項式相關操作,除了直接計算的求導和積分之外,基本使用牛頓迭代進行計算,這樣子即使不記得也可以很好的手玩出來。
首先我們知道多項式的任何一個運算(求逆,開方,\(exp\),\(ln\)),都可以簡單的寫成對於多項式\(B(x)\),以及給定函數\(F(x)\),求解\(F(B(x))\equiv 0(mod\ x^{2^t})\)。
令\(B_t(x)\)表示的一個合法解,在\(t=0\)時,只剩下常數項,我們可以利用常數項的運算很容易的求解出\(B_0\)的值,現在考慮如何用\(B_t\)推出\(B_{t+1}\),這樣子我們就可以用\(B_0\)推出答案。
對於\(F(B_{t+1}(x))\)在\(B_t(x)\)處進行泰勒展開,我們得到:
后面的項數因為在模\(x^{2^{t+1}}\)意義下為\(0\),所以只有前面兩項。
那么化簡之后我們就可以得到遞推式(注意左側的\(F(B_{t+1}(x))\)在模意義下為\(0\)):
多項式運算
求導與積分
很容易,高中數學課上都會講的東西。
記住求完導之后最高項為\(0\),積分后常數項為\(0\)。
多項式求逆
其實可以手玩(拋開牛頓迭代,可以自己遞歸推式子,雖然本質上一樣的)
直接套上牛頓迭代的式子就好了。假設\(A(x)\)是我們要求逆的多項式。
那么有:\(F(B_t(x))=A_{t+1}(x)*B_t(x)-1\equiv 0\)
所以:
然后因為\(B_t(x)\)是\(A_t(x)\)的逆,所以可以進一步化簡。
注意給\(A\)帶下標的意義不大,但是這里特地寫出來是提醒注意下當前層的模數應該是什么。
多項式開根
直接套式子很方便,既然是開根就是:\(F(B_t(x))=B^2_t(x)-A(x)=0\)
推式子就是:
也就是多項式開根的過程中需要套用多項式求逆。
多項式\(ln\)
這個操作並不需要用到牛頓迭代。
所以只需要對於\(A(x)\)求導求逆,乘起來之后就是\(B'(x)\),直接積分就完事了。
多項式\(exp\)
這個推導稍稍復雜一點點。關系式就是\(exp(A(x))=B(x)\),顯然這個東西沒法直接算,而\(exp\)求導之后還有\(exp\),所以我們求\(ln\)把\(exp\)去掉。\(A(x)=ln(B(x))\)。
這樣子也就是\(F(B_t(x))=ln(B_t(x))-A(x)\)
牛頓迭代推式子就是:
多項式快速冪
\(A^k(x)=exp(k*ln(A(x)))\)。
\(emmm\),雖然這東西復雜度是一個\(log\),但是這玩意常數巨大。
多項式除法
這個除法是帶余除法,所以並不能直接求逆解決。
要求的就是給定兩個多項式\(A(x),B(x)\),其項數為\(n,m\)
求解一個\(n-m\)項的多項式\(C(x)\),以及一個小於\(n-m\)項的多項式\(R(x)\)。
滿足:\(A(x)=B(x)*C(x)+R(x)\)。
定義一個操作\(R\),其中\(R\)就是\(Reverse\),\(A^R(x)=x^nA(\frac{1}{x})\)。這個操作說白點就是\(A(x)[x^i]\)對應\(A^R(x)[x^{n-i}]\),也就是把所有的系數給翻轉過來。
然后推式子:
到了這里我們把等號換成同余,把整個式子在模\(x^{n-m+1}\)意義下進行。
所以在模意義下,我們可以利用多項式求逆求解\(C^R(x)=\frac{A^R(x)}{B^R(x)}mod\ x^{n-m+1}\)
那么就有\(R(x)=A(x)-B(x)*C(x)\)。
生成函數
普通型生成函數(OGF)
對於數列\(\{a_i\}\)而言,其\(OGF\)為\(\sum_{i=0}^\infty a_ix^i\)。
和前面多項式的區別在與這個東西是無窮項。
比如說斐波那契數列,寫成\(OGF\)的形式的話就是:
因為是無窮項,所以我們可以推出:
然后就可以把\(F(x)\)寫成和\(x\)相關的一個式子:
這樣子一來我們可以用多項式求逆算出來斐波那契數列,雖然實際上遞推是線性的。這個式子進一步推導可以求解斐波那契數列的通項。
但是如果遞推關系與前\(m\)項相關,並且\(m\)的值不小,那么顯然用類似的方法轉為多項式相關的計算復雜度更加優秀。注意這里求出來的是整個數列。
指數型生成函數(EGF)
對於數列\(\{a_i\}\)而言,其\(EGF\)為\(\sum_{i=0}^\infty \frac{a_i}{i!}x^i\)
從上面的泰勒展開\(e^x\)的時候,我們似乎就見過了這個東西了呢!
兩者之間的一些區別
\(EGF\)本質上和\(OGF\)是類似的,區別在於除了一個階乘。
階乘在計數中意為着什么呢?順序。
那么從中,我們明白了這樣一件事情:\(OGF\)考慮的是組合,意味着相同物品之間沒有區別,而\(EGF\)考慮的是排列,相同之間也要考慮一個順序關系。
所以我們可以來考慮兩個例子:
1.有兩種物品\(A,B\),其中\(A\)物品如果要拿,必須拿奇數個,\(B\)物品如果要拿,必須拿\(3\)的倍數個,求拿\(n\)個物品的方案數。
考慮的是組合,所以我們使用\(OGF\),對於兩種物品構建\(OGF\),可以得到:
\(A(x)=x+x^3+x^5+...\)
\(B(x)=1+x^3+x^6+...\)
那么將兩個生成函數直接乘起來,其第\(n\)項的值就是最終的答案。
一般來說這類題目可以化簡生成函數之后乘起來,約分之后再考慮最終的結果,最后答案的計算方式也無非是兩種,一種是直接算,另外一種是考慮組合意義。
2.有兩種物品,\(A\)物品必須拿奇數個,\(B\)物品必須拿偶數個。然而所有被拿出來的物品之間都是有區別的,求方案數。
發現這個就是一個排列關系了,我們可以任務物品之間的區別就是拿出來的順序。
而奇數個和偶數個的區別和前面使用\(OGF\)是類似的。
\(A(x)=\frac{1}{1!}x+\frac{1}{3!}x^3+\frac{1}{5!}x^5...=\frac{e^x-e^{-x}}{2}\)
\(B(x)=1+\frac{1}{2!}x^2+\frac{1}{4!}x^4+...=\frac{e^x+e^{-x}}{2}\)
兩個\(OGF\)直接乘起來就好了,注意要整理好系數,即一定要寫成\(\frac{a}{i!}x^i\)的形式,因為若干個乘起來之后的結果本質上是一個可重排列\(\frac{n!}{a_1!a_2!...a_k!}\),這樣最終的結果就是\(\frac{a}{i!}x^i\)的系數\(a\)。
常見題目
字符串配對匹配相關
匹配回文串
【BZOJ3160】萬徑人蹤滅
題意:求回文子序列(強制不連續)的個數。
回文串滿足的關系是關於一個回文中心對稱,也就是滿足:\(S[x-a]=S[x+a]\)。不難發現下表\(x-a+x+a=2x\),也就意味着關於這個回文中心如果對稱的話,那么他們的下標和為定制。所以可以分字符考慮,把相同字符設為\(1\),其他字符設為\(0\)。然后卷積一遍之后得到的結果就是關於當前位置對稱的字符對數。
帶通配符的匹配
【BZOJ4503】兩個串
題意:給定兩個串\(S,T\),\(T\)中有通配符,求\(T\)在\(S\)中的出現次數。
如果不考慮通配符的情況,我們可以用\(KMP\)求解。除了\(KMP\)之外,還有一種多項式乘法的求解方法。
考慮什么情況下會匹配上:\(\forall i\in[1,|T|]\ T[i]=S[k-1+i]\),那么\(T\)就從\(S\)的第\(k\)個字符開始匹配上了一次。
發現\(k-1\)是常數,但是兩個下表中\(i\)都是加法,沒法消掉。這個容易,我們把\(T\)串翻轉過來,就有:\(T[|T|-i+1]=S[k-1+i]\),下標之和為\(|T|+k\)為定值。因此我們只需要按照字符集考慮,每次將相同字符全部賦值\(1\),其他字符賦值為\(0\),跑多項式乘法。把所有字符的結果相加,如果某個位置上的和為\(|T|\),證明從這個位置開始能夠匹配上\(|T|\)個字符,也就意味着在\(S\)中,從這個位置開始匹配上了一次\(T\)。這樣子不失為一種好方法,並且每次只需要把通配符都變成\(1\)就完事了。但是缺點也很顯然,復雜度和字符集相關,往往存在一個大常數,對於字符集很大的時候這種方法往往不適用。
實際上,我們考慮一種更加直接的方法。我們明白,只要有一個位置匹配不上,那么從這個位置開頭就匹配不上\(T\)。如果我們給字符集里面的字符編號為\(1..26\)(字符更大就繼續往下去)。如果兩個不匹配的話,證明其編號不同,證明其編號的差不為\(0\)。所以我們只需要考慮從每個位置開始往后的字符的差的絕對值的和,如果這個值是\(0\)的話,顯然不存在沒有匹配上的位置,也就是匹配上了。當然,絕對值很麻煩,我們直接平方就好了。
因此,對於卷積完的第\(k\)位置,其值為\(f(k)=\sum_{i=1}^{|T|}(S[k+i-1]-T[i])^2\)
這個式子直接拆開就變成了\(\sum_{i=1}^{|T|}(S[k+i-1]^2+T[i]^2-2T[i]S[k+i-1])\)
發現是三個部分的和。也就是兩個可以直接計算的常數項加上一個卷積的形式(別忘了后面那個東西怎么卷,上面講過一次的)。
現在加上通配符,顯然通配符也要編一個號,但是因為通配符不可能同時等於字符集里面的所有值,因此我們需要考慮一個額外的方法,使得上述式子的結果只要出現了通配符就為\(0\)。那么這個也很簡單,令通配符編號為\(0\),然后在\(f(k)\)的式子中額外乘上一個\(T[i]\)就好了,只要\(T[i]\)是通配符,也就是\(0\),那么這一項也就變成了\(0\),表示匹配上了。
那么式子變成了:\(f(k)=\sum_{i=1}^{|T|}(S[k+i-1]-T[i])^2T[i]\)
拆開之后也就變成了:\(\sum_{i=1}^{|T|}(T[i]S[k+i-1]^2+T[i]^3-2T[i]^2S[k+i-1])\)
變成了兩個卷積加上一個常數的形式,一樣的直接解決即可。
類似題:【BZOJ4259】殘缺的字符串
這道題里面\(S\)也包含了通配符,因此在\(f(k)\)的式子中額外乘一個\(S[i]\)就好了。
本質上完全一樣的兩道題目。另外一點,就是關注字符集的大小,對於很小的字符集而言暴力是可行的。
總結
對於這一類字符串匹配的題目,回文直接乘,順序匹配翻轉后乘,利用匹配的式子考慮倒地應該怎么乘。同時,根據要匹配的東西,考慮是分字符集還是直接編號,如果直接編號應該怎么考慮統配分等匹配問題。想清楚如何給多項式賦值,最終乘出來的是什么東西就好了。
加速計算
也就是說,在這一類問題里面,多項式的乘法並不占主要地位,主要用它來加速乘法。
比如計算\(10^5\)位的高精度乘法,顯然就要使用\(FFT\),而不能暴力\(O(n^2)\)的乘法。
還有比較直接的題目,比如【BZOJ4827】禮物,【BZOJ3527】力。這兩個題目都是推導式子時候發現要計算特定的一項,但是直接暴力算很慢,發現可以用\(FFT\)優化。
還有比較常見的是第一類斯特林數和第二類斯特林數等一些數列的快速計算問題。
比如【BZOJ4555】求和,推出用第二類斯特林數的計算式之后,唯一需要考慮的就是求第二類斯特林數,而\(O(n^2)\)的遞推太慢,因此直接用\(FFT\)計算。
還有像\(dp\)太慢所以轉成多項式乘法的【BZOJ3992】序列統計。
這類問題中的\(FFT\)只是一個優化復雜度的工具而已,並不是考察的重點(雖然如果你推不出多項式的式子的話根本做不出來的說)。一般而言都可以比較容易的寫出一個復雜度在\(O(n^2)\)的東西,然后深思熟慮看出來多項式卷積之后優化到了\(O(nlogn)\)。
生成函數相關運用
這一類問題就多得去了,要憑借的主要還是自己的腦子。
生成圖的計數
注意有標號和沒標號的區別,這決定了使用\(OGF\)還是\(EGF\)。
想清楚生成圖之間的邊和點的關系,因為生成圖計數問題中,點的數量顯然是已知的,所以我們需要考慮的是邊的情況。
【BZOJ3456】城市規划
題意:求\(n\)個點無向連通圖的個數
顯然\(n\)個點是有標號的,因此我們最終構建出來的生成函數一定是\(EGF\)。
設\(n\)個點的圖的個數為\(g(n)=2^{C_n^2}\)。設\(n\)個點的無向連通圖的個數為\(f(n)\)。
考慮這兩者之間的關系,我們可以推出式子\(g(n)=\sum_{i=1}^{n}C_{n-1}^{i-1}f(i)g(n-i)\)。
可以想想這種關系是怎么來的,我們枚舉\(1\)號點所在的無向連通圖(無論如何\(1\)都會在一個連通塊里),然后剩下的部分顯然是隨意選擇。注意這里的關系是怎么推導的:確定特殊點\(1\)號節點的相關信息,然后寫出卷積的關系式。這是一個很常用的方法,但是注意一點,並不是無論何時都是選擇\(1\)號點所在的連通塊,能夠特殊選擇是因為你選擇了\(1\)號點和其他點是沒有區別的,接下來就有不會選擇\(1\)號點相關信息的題目。
接下來這題就可以接着往下推了,通常這種式子看見組合數直接拆,因為組合數拆完之后有\(m!(n-m)!\)的形式,往往可以往卷積的方向靠。
拆開后得到了\(g(n)=\sum_{i=1}^n\frac{(n-1)!}{(i-1)!(n-i)!}f(i)g(n-i)\)。
這個式子我們對其每一項進行一定的分類,我們就可以寫成一個很好看的形式\(\frac{g(n)}{(n-1)!}=\sum_{i=1}^n\frac{f(i)}{(i-1)!}\frac{g(n-i)}{(n-i)!}\)
這樣子的形式很明顯就是\(EGF\)了。
令\(F(x)=\sum_{i=1}\frac{f(i)}{(i-1)!}x^i,G1(x)=\sum_{i=1}\frac{g(i)}{(i-1)!}x^i,G2(x)=\sum_{i=0}\frac{g(i)}{i!}x^i\)
那么上面的那個式子就可以寫成\(G1(x)=F(x)G2(x)\)。
這樣子再往下走就是\(F(x)=\frac{G2(x)}{G1(x)}\),只需要多項式求逆就可以得出結果。
從這道題目中就可以很明顯的感受到難點在哪里,並不是在於最終的生成函數,而是在於前面如何列出關系式來進行進一步的推導。這里經常可以搭配上多項式開方和求逆。
背包問題
顯然背包的轉移可以寫成生成函數的形式。這里考慮的是背包的計數問題。轉移顯然是\(f[i]=\sum f[i-V_k]\)
比如說\(01\)背包,統計恰好放了體積為\(V\)的方案數,那么把\(f\)數組看成生成函數,那么我們可以把每個物品寫成\(1+x^{V_i}\)的形式,那么最終的答案就是所有物品多項式的成績。考慮這里怎么乘,如果一個個乘的話復雜度是\(O(n^2)\)的。然而這個東西是可以做到更加優秀的復雜度的。先看看更加極端的背包問題。
比如完全背包,物品個數不受限制,那么就是這道題目【洛谷4389】付公主的背包。
顯然每個重量的物品構成的生成函數是\(\sum_{i=0}^{\infty}[i\%V_k=0]x^i\),化簡一下可以寫成\(\frac{1}{1-x^{V_k}}\)。
考慮怎么求\(\prod \frac{1}{1-x^{V_k}}\)。我們直接乘顯然束手無策,然而我們可以把每一項分開求\(ln\),求和之后再\(exp\)回去就好了。
考慮如何求解這個東西的\(ln\)。因為\(ln(\frac{1}{x})=-ln(x)\),所以只考慮求\(ln(1-x^V)\)。
推導如下(\(dx\)這種東西懶得寫了):
所以我們求解的\(ln\)化簡之后就是\(\sum_{i=1}^{\infty}\frac{x^{Vi}}{i}\)。
那么只需要開一個桶記錄一下每一個重量的物品的個數,然后暴力枚舉其重量的倍數,把對應的\(1/i\)的貢獻加進去,最后多項式\(exp\)就好了。
(我上面那篇題解里面的寫法是直接算\(1-x^V\)的貢獻,最后再求逆,事實上並不需要那樣子)
那么\(01\)背包或者多重背包之類的問題可以類似解決。
套路1
【洛谷4705】玩游戲
題意:求\((a_x+b_y)^t\)的期望。
對於給定的\(t\)而言,我們可以直接二項式定理展開。
所以實際上我們要求的東西就只有\(\sum a^i,\sum b^i\)。這兩相同,只考慮其中一樣。
考慮前面如何算的多項式的\(ln\)。
我們換成\((1+ax)\)再來計算。
構造\(F(x)=\prod(1+a_ix)\),那么
因此,我們只需要求出\(ln(F(x))\),再對於每一項還原一下系數就是我們要求的的東西了。
顯然\(F(x)\)可以分治+\(FFT\)求出來,直接求\(ln\)就做完了。
最后再回到題目要求的東西,只需要再做一次卷積就好了。