FWT 學習總結

2022-05-31 06:30:09 字數 3903 閱讀 5914

我理解的fwt是在二元運算意義下的卷積

目前比較熟練掌握的集合對稱差卷積

對於子集卷積和集合並卷積掌握不是很熟練(挖坑ing)

那麼就先來談一談集合對稱差卷積吧

所謂集合對稱差卷積

就是h(i)=sigma(g(j)*f(k))(j^k=i)

首先乙個很顯然的事情是如下結論:

證明就是如果s是空集,答案為1,否則設存在元素v,則(s交t)和(s交t^v)兩兩相消配對

答案為0

由於j^k=i,則一定存在j^k^i=0,所以我們可以用上面的式子化簡卷積

式子的化簡顯然是正確的,就是將判斷符號帶入之後利用乘法分配律分配

這樣我們定義:

顯然利用反演我們可以得到

至於證明,我們將上面的式子帶入下面的式子就可以啦

之後我們再下面的式子代入最上面的式子

可以得到

這也就意味著如果我們可以完成快速沃爾什變換和逆變換

我們就可以在o(n)的時間內把他們乘起來

至於變換的過程,我們利用遞推的思想可以得到:

說的簡單的一點就是我們fwt的時候利用倍增思想

設左邊得到的集合冪級數為tf0

設右邊得到的集合冪級數為tf1

合併之後得到的集合冪級數為(tf0+tf1,tf0-tf1)

至於逆變換

得到的集合冪級數為((tf0+tf1)/2,(tf0-tf1)/2)

由於有除法,所以fwt不能對特徵為2的集合冪級數使用

以上截圖自vfk**

然後一道眾人皆知的例題是srm 518 nim

在[1,l]中可以重複的選取k個質數,問有多少種方案使得他們異或和為0

我們可以構造集合冪級數,之後我們會發現選取兩個我們只需要fwt一次就可以了

選取k個我們就可以使用快速冪了,注意這裡並不是對fwt做快速冪

由於集合冪級數並沒有次數界的問題,所以可以直接對每個點做快速冪

之後再fwt還原即可

#include#include#include#include#includeusing namespace std;

typedef long long ll;

const int maxn=200010;

const int mod=1e9+7;

int k,l,m,inv;

bool vis[maxn];

int p[maxn],cnt=0;

int f[maxn];

int pow_mod(int v,int p)return tmp;

}void get_prime()

}return;

}void fwt(int *a,int n,int flag)return tmp;

}void fwt(int *a,int n,int flag)

fwt(f,n,1);

for(int j=0;j=mod)ans-=mod;

}printf("%d\n",ans);

}return 0;

}

codeforces 259 div1 d

有2^m個點,每個點上有資訊,每個時刻每個點會分別向跟他海明碼距離為k的點傳b(k)次他自己的資訊

問t時刻後每個點的資訊量

考試題的加強版本,而且資料範圍非常全

首先模數的問題可以同考試題的做法解決

首先做法1,我們考慮每個時刻的傳輸並用集合冪級數表示

設集合冪級數f(i)表示輸入陣列

設集合冪級數g(j),滿足g(j)=b(num(j)) num(j)即j的二進位制表示中1的個數

則下一時刻可得h(k)=sigma(f(i)*g(j))(i^j=k)

之後t時刻的話快速冪即可,由於要寫快速乘,時間複雜度o(nlog^2n)

用一些奇技淫巧優化快速乘做到o(nlogn)

#include#include#include#include#includeusing namespace std;

typedef long long ll;

int n,m;

int num[1050010];

ll t,mod;

ll b[22];

ll f[1050010],g[1050010];

inline read(ll &num)

inline ll mul(ll a,ll b)

ll pow_mod(ll v,ll p)return tmp;

}void fwt(ll *a,int n,int flag)

} }return;

}int main()

fwt(f,n,0);fwt(g,n,0);

for(int i=0;i我們還有更優的做法

由於每個點的貢獻是可分的,我們不妨考慮每個點t時刻內的貢獻係數

同考試題可以證明根據海明碼距離我們可以分出m+1個等價類

問題在於我們要搞出單次轉移的係數,之後矩陣乘法就可以了

我們考慮海明碼距離j對海明碼距離i的轉移,顯然i中對海明碼距離貢獻為1的點有i個,貢獻為0的點有m-i個

設k=i-j,我們選取貢獻為1的點x個並取反,選取貢獻為0的點y個並取反,一定滿足y=x-k

這樣操作後兩個點的海明碼距離為x+y

係數的貢獻顯然就是b(x+y)*c(i,x)*c(m-i,y)

其中c是組合數,我們列舉x算貢獻就可以了

我們矩陣乘法搞出係數來了之後同考試題的做法一樣做fwt就可以了

時間複雜度o(m^3logn+nlogn)

#include#include#include#include#includeusing namespace std;

typedef long long ll;

int m,n;

int num[1050010];

ll t,mod;

ll c[22][22];

ll f[1050010],g[1050010];

ll b[22];

struct matrix

}a,ans;

void pre_c()

}return;

}ll mul(ll a,ll b)

a<<=1;

if(a>=mod)a-=mod;

b>>=1;

}return s;

}matrix operator *(const matrix &a,const matrix &b)

} }return c;

}matrix pow_mod(matrix v,ll p)return tmp;

}void fwt(ll *a,int n,int flag)

} }return;

}int main()

} }a=pow_mod(a,t);

ans.a[0][0]=1;

ans=ans*a;

for(int i=0;i>1]+(i&1);

g[i]=ans.a[0][num[i]];

} fwt(f,n,0);fwt(g,n,0);

for(int i=0;i總結:如何想到fwt?

1、題目中的形式是類似卷積一樣的東西,樸素做法o(n^2)算貢獻

2、貢獻過程是二元運算關係

3、通常情況下算異或和為0神馬的qaq

FWT 學習筆記

因為考試遇見了所以來學習一下,雖然好像之前有個神仙來講過,但啥都記不到了 相比fft的ci j k iaj b kc i sum a j b k ci j k i aj bk fwt其實很簡單 c i j k iaj b kc i sum a j b k ci j k i aj bk 其中 bigo...

FWT 學習筆記

好久以前寫的,先粘上來 定義陣列 n 2 k a a 0,a 1,a 2,a 3,a 令 a 0 a 0,a 1,a 2,a 且 a 1 a a a 即 a 0 為沒有最高位的部分,a 1 為有二進位制最高位的部分 a 可以用 a 表示 定義運算 a b a 0 b 0,a 1 b 1,a n b ...

FWT背板筆記

板子 背板子.jpg fwt 用於解決這樣的問題 c i sum a j times b k 其中 bigoplus 是一種二元運算子,如 or,and,xor 首先我們直接做複雜度顯然高達 4 n 或許可以利用一些列舉子集的技術做到 3 n 但是還是非常難以接受 於是我們考慮能否像 fft 那樣構...