码迷,mamicode.com
首页 > 其他好文 > 详细

FWT 等总结

时间:2019-07-28 09:43:14      阅读:124      评论:0      收藏:0      [点我收藏+]

标签:比较   math   toc   利用   ==   tle   line   limit   问题   

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;
            }
        }
    }
}

FST:子集卷积
\(h(i)=\sum\limits_{j or k=i且j and k=0} f(j)*g(k)\)
比或卷积多了一个限制。
我们发现,设\(s(i)\)表示\(i\)的二进制表示中1的个数,那么如果\(i\|j=k,i\&j=0\),则\(s(i)+s(j)=s(k)\)
利用这个性质,我们可以加一维表示\(s\),在\(F*G\)时考虑\(s\)的限制。
时间复杂度:\(O(nlog^2n)\)
代码:

for(int i=0;i<len;i++)
{
    for(int j=0;j<17;j++)
    {
        if(i&(1<<j))
            sl[i]+=1;
    }
}
for(int i=0;i<len;i++)
    a[sl[i]][i]=sz[i];
for(int i=0;i<18;i++)
    fwtor(a[i],len);
for(int i=0;i<18;i++)
{
    for(int j=0;i+j<18;j++)
    {
        for(int k=0;k<len;k++)
            h1[i+j][k]=(h1[i+j][k]+1ll*a[i][k]*a[j][k])%md;
    }
}
for(int i=0;i<18;i++)
    ifwtor(h1[i],len);
for(int i=0;i<len;i++)
    ab[i]=h1[sl[i]][i];

FWT 等总结

标签:比较   math   toc   利用   ==   tle   line   limit   问题   

原文地址:https://www.cnblogs.com/lnzwz/p/11257691.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!