我理解的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;codeforces 259 div1 dtypedef 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;
}
有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 那樣構...