F W T FWT FWT是利用分治的方法来求解多项式诸如
C k = ∑ i + j = k A i ⊕ B j ( ⊕ = a n d , x o r , o r ) C_k=\sum_{i+j=k}A_i \oplus B_j ~ (\oplus=and,xor,or) Ck=i+j=k∑Ai⊕Bj (⊕=and,xor,or)
的运算。相比于朴素的 O ( n 2 ) O(n^2) O(n2)做法, F W T FWT FWT的时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)
考虑 F F T FFT FFT中系数转点值的方法是否能利用到求上述式子中来。
即对于这样的一个多项式我们能否也能做到用以下三步求解:
那么我们考虑如何来构建 A ′ , B ′ A^{'},B^{'} A′,B′和 C ′ C^{'} C′,使得达到第二、三步骤。
考虑多项式 l e n = 2 len=2 len=2的情况。那么
C 0 = A 0 B 1 + A 1 B 0 + A 0 B 0 C_0=A_0B_1+A_1B_0+A_0B_0 C0=A0B1+A1B0+A0B0
C 1 = A 1 B 1 C_1=A_1B_1 C1=A1B1
发现若 A 1 ′ = A 1 , B 1 ′ = B 1 A_1^{'}=A_1,B_1^{'}=B_1 A1′=A1,B1′=B1,那么 C 1 = A 1 ′ B 1 ′ C_1=A_1^{'}B_1^{'} C1=A1′B1′,但对于 C 0 C_0 C0则不好考虑,因为 C 0 C_0 C0中 A , B A,B A,B的下标皆有 0 , 1 0,1 0,1,但少了个 A 1 B 1 A_1B_1 A1B1,我们不好构造出 A 0 ′ , B 0 ′ A_0^{'} ,B_0^{'} A0′,B0′ 来使得 C 0 = A 0 ′ B 0 ′ C_0=A_0^{'}B_0^{'} C0=A0′B0′。
那么我们试着写出以下式子:
A 0 ′ = A 0 + A 1 A_0^{'}=A_0+A_1 A0′=A0+A1
B 0 ′ = B 0 + B 1 B_0^{'}=B_0+B_1 B0′=B0+B1
C 0 ′ = A 0 B 1 + A 1 B 0 + A 0 B 0 + A 1 B 1 C_0^{'}=A_0B_1+A_1B_0+A_0B_0+A_1B_1 C0′=A0B1+A1B0+A0B0+A1B1
C 1 ′ = A 1 B 1 C_1^{'}=A_1B_1 C1′=A1B1
那么对于第二个步骤即可解决,同时对于步骤三,我们 I F W T IFWT IFWT时只要让 C 0 = C 0 ′ − C 1 ′ , C 1 ′ C_0=C_0^{'}-C_1^{'},C_1^{'} C0=C0′−C1′,C1′不变即可( I F W T IFWT IFWT对于 A , B A,B A,B的变换也是对的)。
这样时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)
同样考虑多项式 l e n = 2 len=2 len=2的情况,那么
C 0 = A 1 B 1 + A 0 B 0 C_0=A_1B_1+A_0B_0 C0=A1B1+A0B0
C 1 = A 0 B 1 + A 1 B 0 C_1=A_0B_1+A_1B_0 C1=A0B1+A1B0
同样要满足上面的三个步骤,那么 C 0 ′ C_0^{'} C0′和 C 1 ′ C_1^{'} C1′都不好求,所以我们的 C ′ C^{'} C′要都包含 4 4 4个式子。
我们试着写出以下式子:
C 0 ′ = A 0 B 0 + A 1 B 1 + A 0 B 1 + A 1 B 0 C_0^{'}=A_0B_0+A_1B_1+A_0B_1+A_1B_0 C0′=A0B0+A1B1+A0B1+A1B0
C 1 ′ = A 0 B 0 + A 1 B 1 − A 0 B 1 − A 1 B 0 C_1^{'}=A_0B_0+A_1B_1-A_0B_1-A_1B_0 C1′=A0B0+A1B1−A0B1−A1B0
A 0 ′ = A 0 − A 1 A_0^{'}=A_0-A_1 A0′=A0−A1
A 1 ′ = A 0 + A 1 A_1^{'}=A_0+A_1 A1′=A0+A1
B 0 ′ = B 0 − B 1 B_0^{'}=B_0-B_1 B0′=B0−B1
B 1 ′ = B 0 − B 1 B_1^{'}=B_0-B_1 B1′=B0−B1
那么第二个步骤即可解决,同时对于步骤三,我们 I F W T IFWT IFWT时的操作即为使 C 0 = ( C 0 ′ + C 1 ′ ) / 2 , C 1 = ( C 0 ′ − C 1 ′ ) / 2 C_0=(C_0^{'}+C_1^{'})/2,C_1=(C_0^{'}-C_1^{'})/2 C0=(C0′+C1′)/2,C1=(C0′−C1′)/2即可
接下来的或运算是同样的方法,这里就不写了。
对于 C 0 , C 1 C_0,C_1 C0,C1。
若都为两项则相加,相减。
若一个为 3 3 3项,一个为 1 1 1项,则把 3 3 3项的加一个项
接着怎么变过去的就怎么变回来。
最后洛谷FWT模板的代码如下:
/*******************************
Author:galaxy yr
LANG:C++
Created Time:2019年08月28日 星期三 19时44分29秒
*******************************/
#include
#include
using namespace std;
const int mod=998244353;
const int maxn=1<<18;
long long inv2,n,len=1,a[maxn],b[maxn],c[maxn];
void FWT(long long * a,long long n,long long on,int opt)//opt=0 or,opt=1 and opt=3 xor
{
static long long tmp;
for(int len=2;len<=n;len<<=1)
for(int i=0;i<n;i+=len)
for(int j=i;j<i+len/2;j++)
{
if(opt==0)
{
if(on) a[j+len/2]+=a[j];
else
a[j+len/2]-=a[j];
}
if(opt==1)
{
if(on)
a[j]+=a[j+len/2];
else
a[j]-=a[j+len/2];
}
if(opt==2)
{
if(on)
tmp=a[j], a[j]-=a[j+len/2], a[j+len/2]+=tmp;
else
tmp=a[j],a[j]=(a[j]+a[j+len/2])*inv2,a[j+len/2]=(a[j+len/2]-tmp)*inv2;
}
a[j]%=mod; a[j+len/2]%=mod;
}
}
long long ksm(long long a,long long b)
{
long long ans=1; a%=mod;
while(b)
{
if(b&1)ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
int main()
{
//freopen("FWT.in","r",stdin);
//freopen("FWT.out","w",stdout);
inv2=ksm(2,mod-2);
cin>>n; len<<=n;
for(int i=0;i<len;i++)cin>>a[i];
for(int i=0;i<len;i++)cin>>b[i];
for(int i=0;i<3;i++)
{
FWT(a,len,1,i);
FWT(b,len,1,i);
for(int j=0;j<len;j++)
c[j]=(a[j]*b[j])%mod;
FWT(c,len,0,i);
FWT(a,len,0,i);
FWT(b,len,0,i);
for(int j=0;j<len;j++)
cout<<(c[j]%mod+mod)%mod<<' ';
cout<<endl;
}
return 0;
}