FWT可以解决位运算卷积问题。
即\(h(i)=\sum\limits_{j⊕k=i} f(j)*g(k)\),其中“⊕”表示位运算。
与卷积:
定义\(f\)到\(F\)的变换:\(F(i)=\sum\limits_{j\&i==i}^{ }f(j)\)。
这样,若\(h(i)=\sum\limits_{j and k=i} f(j)*g(k)\),则\(H(i)=F(i)*G(i)\)。
变换方法:就是按照长度为\(2^i\)分段,把每段的后半部分加到前半部分(1对0有额外贡献)。
逆变换就是减回去。时间复杂度:\(O(nlogn)\)。
代码:
void fwtand(int sz[132000],int n)
{
for(int i=2;i<=n;i=(i<<1))
{
for(int j=0;j<n;j+=i)
{
for(int k=0;k<(i>>1);k++)
sz[j+k]=(sz[j+k]+sz[j+(i>>1)+k])%md;
}
}
}
void ifwtand(int sz[132000],int n)
{
for(int i=2;i<=n;i=(i<<1))
{
for(int j=0;j<n;j+=i)
{
for(int k=0;k<(i>>1);k++)
sz[j+k]=(sz[j+k]-sz[j+(i>>1)+k]+md)%md;
}
}
}
或卷积:
与“与卷积”类似。
定义\(f\)到\(F\)的变换:\(F(i)=\sum\limits_{j|i==i}^{ }f(j)\)。
这样,若\(h(i)=\sum\limits_{j or k=i} f(j)*g(k)\),则\(H(i)=F(i)*G(i)\)。
变换方法:就是按照长度为\(2^i\)分段,把每段的前半部分加到后半部分(0对1有额外贡献)。
逆变换就是减回去。时间复杂度:\(O(nlogn)\)。
代码:
void fwtor(int sz[132000],int n)
{
for(int i=2;i<=n;i=(i<<1))
{
for(int j=0;j<n;j+=i)
{
for(int k=0;k<(i>>1);k++)
sz[j+(i>>1)+k]=(sz[j+(i>>1)+k]+sz[j+k])%md;
}
}
}
void ifwtor(int sz[132000],int n)
{
for(int i=2;i<=n;i=(i<<1))
{
for(int j=0;j<n;j+=i)
{
for(int k=0;k<(i>>1);k++)
sz[j+(i>>1)+k]=(sz[j+(i>>1)+k]-sz[j+k]+md)%md;
}
}
}
异或卷积:
这个比较常用。
定义\(f\)到\(F\)的变换:\(F(i)=\sum\limits_{j=0}^{2^n-1}(-1)^{bit(j and i)}f(j)\)。
这样,若\(h(i)=\sum\limits_{j xor k=i} f(j)*g(k)\),则\(H(i)=F(i)*G(i)\)。
变换方法:就是按照长度为\(2^i\)分段,把每段的前半部分变为前半部分加后半部分,
后半部分变为前半部分减后半部分。
逆变换就是相当于已知\(a+b=x,a-b=y\),则\(a=(x+y)/2,b=(x-y)/2\)。
就是正变换再除以2。
时间复杂度:\(O(nlogn)\)。
代码:
void fwtxor(int sz[132000],int n)
{
for(int i=2;i<=n;i=(i<<1))
{
for(int j=0;j<n;j+=i)
{
for(int k=0;k<(i>>1);k++)
{
int a=sz[j+k],b=sz[j+(i>>1)+k];
sz[j+k]=(a+b)%md;
sz[j+(i>>1)+k]=(a-b+md)%md;
}
}
}
}
void ifwtxor(int sz[132000],int n)
{
for(int i=2;i<=n;i=(i<<1))
{
for(int j=0;j<n;j+=i)
{
for(int k=0;k<(i>>1);k++)
{
int a=sz[j+k],b=sz[j+(i>>1)+k];
sz[j+k]=1ll(a+b)inv%md;
sz[j+(i>>1)+k]=1ll(a-b+md)inv%md;
}
}
}
}
```