多項式全家桶 Part 2 多項式位運算

2022-07-24 09:48:08 字數 3476 閱讀 6620

我們可以用fft、ntt計算多項式乘法:

\[c(k)=\sum_a(i)*b(j)

\]但是計算不了這種玩意:

\[c(k)=\sum_a(i)*b(j)

\]或者這種東東:

\[c(k)=\sum_a(i)*b(j)

\]或者這種:

\[c(k)=\sum_a(i)*b(j)

\]所以乙個演算法應著時代的需求誕生了:

過程多項式或運算是用來快速做這條式子的:

\[c(i)=\sum_a(j)*b(k)

\]其具體思想其實和fft差不多,首先先把多項式a轉化為點值表達,然後再快速運算。

然後再轉回插值。這樣完成乙個演算法流程。然鵝我們如何快速dft這個醜陋的式子呢?

拿出流程圖:

一樣三部曲:

如何找出類似於fft的點值乘法

我們這裡並不考慮去什麼點值插值,我們只需要一些簡單的數論變換即可得到我們所要求的東東。

同樣的思路,考慮把a轉化成另外乙個東東,假設叫做\(fwt(a)\),其滿足可以快速求出\(fwt(c)=fwt(a)*fwt(b)\)。算出來之後還可以快速轉化回\(a\),也就是\(ifwt\)逆變換。

首先我們觀察柿子:\(i|j=k\),我們發現,由於是或運算,看到或運算就應該拆位(這個套路真的好用)。

那麼拆完位就可以得到乙個結論:如果將k拆位完後,得到1位置集合。那麼i的1位置集合與j的1位置集合即為k的1位置集合的子集。

得到這個結論後,我們即可構造:

\[fwt(a)=\sum_a(j)

\]這條柿子的意義即為:j的1的位置集合為i的1的位置集合的子集。

(其實為什麼這樣構造,理由是要去進行一波反演得到的。這裡就8推了,記結論)

那麼如果把\(fwt(a)\)和\(fwt(b)\)乘起來會變成什麼呢?

\[fwt(a)*fwt(b)=\sum_a(j)*\sum_b(k)\\=\sum_\sum_a(j)*b(k)\\=\sum_a(j)*b(k)\\=fwt(c)

\]那麼就有方向了。如果現在已經得到乙個新的東東\(fwt(a)\),那麼答案就可以在\(o(n)\)時間內求出。

問題是如何快速求出\(fwt(a)\)。

如何找出fwt正變換

同樣考慮像fft一樣分治求之。

那麼遞迴可能會慢。怎麼辦?能不能像fft一樣把下標取出掘其規律?

那就試試。(逝世)

還記得這張神圖把。

考慮設a0表示當前分治到的當前位為0的序列,a1表示當前分治到的當前位為1的序列。

再回顧\(fwt(a)\)的定義:

\[fwt(a)=\sum_a(j)

\]那麼可以知道在對應位置的a0永遠是a1的子集。

所以可以寫成這個式子:

\[fwt(a)=(fwt(a0),fwt(a1+a0))

\]其中\(a1+a0\)表示對應位置相加。

於是我們的fwt正變換就完成了。

如何找出fwt逆變換(ifwt)

這個直接理解還挺簡單的。我們發現,由於fwt是求子集和,現在要求的是當前值。

所以把子集減去即可。

於是寫成這個式子:

\[fwt(a)=(fwt(a0),fwt(a1-a0))

\]後話

話說其實fwt可以有多種方法來看。

網上有的說這其實是fmt(快速莫比烏斯變換)

(其實這玩意我真的沒搞懂它和反演有什麼區別),我一開始看vfleaking的反演覺得是個子集反演用分治來做。

然鵝比較簡單的理解就是從位運算的意義上理解,可以推柿子來比較嚴謹地去證明,當然也可以我這樣直接理解地去推結論。

乙個問題有多種角度去看,有時是一件好事,有時卻是一件壞事,因為可能會莫衷一是,一開始學會陷入理解的困境。

不管怎樣,還是挺好玩的。就好像3blue1brown讓我學會的乙個道理,去發現數學的美,而不是去死板地認為這個就是乙個工具。

誒我tm怎麼莫名其妙就放了怎麼多沒用的屁話

結論\[fwt(a)=(fwt(a0),fwt(a1+a0))

\]\[ifwt(a)=(ifwt(a0),ifwt(a1-a0))

\]**

void orfwt(int a,int inv)

a(j)-\sum_a(j)

\]這玩意當然滿足\(fwt(c)=fwt(a)*fwt(b)\)

理由就是把這條柿子帶進去,然後再瞎換一下即可得到。

當然,在推柿子的時候可能要用到乙個結論:\(f(i\&j)\ xor\ f(i\&k)=f(i\&(j\ xor\ k))\)

這裡8推了。

如何找出fwt正變換

考慮如何求\(fwt(a)\)

觀察柿子:

\[fwt(a)=\sum_a(j)-\sum_a(j)

\]由於是位運算,那麼繼續套路,拆位。

考慮當前第i位,這一位就有4種情況:

這意味著什麼呢?

假如把\(fwt(a)\)取個絕對值(當然這樣計算最後的答案是不會改變的),那麼就可以把奇偶性改變都看做是減去貢獻,反之則為加上貢獻。

那麼正變換就得到了:

\[fwt(a)=(fwt(a0+a1),fwt(a0-a1))

\]如何找出fwt逆變換(ifwt)

逆變換一樣簡單,把上面的計算貢獻反過來即可。

\[ifwt(a)=(\frac2,\frac2)

\]結論

\[fwt(a)=(fwt(a0+a1),fwt(a0-a1))

\]\[ifwt(a)=(\frac2,\frac2)

\]**

void xorfwt(int a,int inv)

{ long long op,oq,kk;

for (int len=2;len<=m;len<<=1)

{ int mid=len/2;

for (int j=0;j#include #include #include using namespace std;

const long long mo=998244353;

const long long inv2=499122177;

int n,m,a[131072],b[131072],ja[131072],jb[131072];

void orfwt(int a,int inv)

{ long long op,oq;

for (int len=2;len<=m;len<<=1)

{ int mid=len/2;

for (int j=0;j學習資料:

多項式全家桶

眾所周知,生成函式是乙個十分強大的東西,許多與多項式相關的演算法也就應運而生了,在這裡選取幾種較為簡單的演算法做乙個介紹.p.s.這篇文章在去年noi前已經完成了一半,現在筆者將其補充完整後發出,同時也為了紀念那一段美好的時光。已知 f x 求 g x 使得 f x g x equiv 1 mod ...

多項式全家桶

已知多項式 g x 求 f x 滿足 g f x equiv 0 pmod 假設我們有乙個 f 0 x 滿足 g f 0 x equiv 0 pmod rceil 由定義可知 f x f 0 x equiv 0 pmod rceil rightarrow forall k ge 2,left f x...

多項式全家桶

include include includeusing namespace std const double pi acos 1.0 const int maxn 1e7 5 inline int read int n,m,len,lim 1 int r maxn struct complex c...