FWT

picks博客作为参考

FWT能干什么:

FWT可以对于两个数组a和b,求出他们的位运算卷积c,使得c[k]= a [ i ] b [ j ] (对于所有的i和j 满足 i位运算j等于k )

我们先讲与卷积和或卷积,最后再讲异或卷积

一个简单的问题:

给定数组a,求数组b,使得b[i]= a [ j ] (对于所有的j满足j|i==i)

我们考虑按位分治,先把数组长度用0补到2的幂

先不考虑最高位,那么我们可以把长度为2^l的原数组按最高位为0和最高位为1分为两部分

递归处理这两部分,假设我们现在已经得到了对于所有最高位为0的下标求的b数组b0和对所有最高位为1的下标求的b数组b1,他们的长度均为2^(l-1)

那么我们现在考虑最高位的影响。我们把计入最高位影响之后的长度为2^l的b数组分为左右两部分,记为bn0和bn1,即最高位为0的部分和最高位为1的部分,他们的长度也都为2^(l-1)

那么对于任意的i和j,如果j|i不等于i,给i和j添加一个最高位之后的j|i一定也不等于i,所以bn0[i]和bn1[i]只与b0[i]和b1[i]有关

而如果j|i等于i,那么假设给i添加最高位ih,给j添加最高位jh,添加最高位后j|i是否等于i就只与jh|ih是否等于ih有关

那么我们显然就可以得到bn0[i]=b0[i],bn1[i]=b0[i]+b1[i]

觉得不显然可以YY一下

如果是要求使得b[i]= a [ j ] (对于所有的j满足j&i==i)的话,那么就是bn0[0]=b0[i]+b1[i],bn1[i]=b1[i]

这样的话我们就可以在n log n的时间内求出b数组

我们会发现这个做法事实上就是或卷积的正变换

在后面我们也将求b数组叫做正变换

或卷积、与卷积的原理:

或卷积和与卷积事实上基于如下原理:

首先: k | k = k k&k=k

则有:

i&k=k并且j&k=k等价于 (i&j)&k=k 原理一

i|k=k并且j|k=k等价于 ( i | j ) | k = k 原理二

那么如果要求数组a和数组b的与卷积或者或卷积,我们就只需要求出a的正变换a’,即 a [ i ] = b [ j ] (j&i==i) 和b的正变换b’,即 b [ i ] = a [ j ] (j&i==i) ,然后另 c [ i ] = a [ i ] b [ i ] ,由原理一可得,那么容易发现c’就是a和b的卷积做正变换的结果

那么我们现在就只需要考虑给你一个正变换之后的结果,如何做逆变换求出原数组

逆变换:

逆变换的过程事实上就是一个解密的过程,而正变换相当于加密

我们还是按位考虑,在正变换的时候,我们令 b n 0 [ i ] = b 0 [ i ] b n 1 [ i ] = b 0 [ i ] + b 1 [ i ] 那么如果你现在知道的是bn0和bn1,那么容易知道 b 0 [ i ] = b n 0 [ i ] b 1 [ i ] = b n 1 [ i ] b n 0 [ i ]

与运算的逆变换同理

然后再递归左右两边即可

你可能会产生疑惑:在进行正变换的时候我们先递归左右两边再考虑这一位,那么在逆变换的时候是否需要先考虑这一位再递归两边呢

而事实上因为每一位都是等价的,所以逆变换的时候一样可以先递归再考虑这一位,顺序无所谓

异或卷积:

异或这个东西和与还有或就不太一样……所以我们YY了挺长时间才YY明白异或卷积是个什么鬼畜

异或卷积基于如下原理:

定义i和j之间的奇偶性为:i&j中为1的位数的奇偶性,若为偶数则奇偶性是0,若为奇数则奇偶性是1

那么i和k的奇偶性异或j和k的奇偶性等于i^j和k的奇偶性

证明显然,YY一下即可

那么我们令数组a做正变换之后的数组b的意义是:b[i]= a [ j ] a [ k ] (j满足i和j的奇偶性==0 ,k满足i和k的奇偶性==1)

那么如果要求a和b的异或卷积c,另a做完正变换为a’,b做完正变换为b’, c [ i ] = a [ i ] b [ i ] ,那么c’恰好就是c做正变换之后的结果

证明显然,如果觉得不显然可以稍微YY一下就明白了

那么就考虑一下如何做正变换和逆变换就好了,还是按位分治

在正变换的时候,令 b n 0 [ i ] = b 0 [ i ] + b 1 [ i ] b n 1 [ i ] = b 0 [ i ] b 1 [ i ]
在逆变换的时候,令 b 0 [ i ] = ( b n 0 [ i ] + b n 1 [ i ] ) / 2 b 1 [ i ] = ( b n 0 [ i ] b n 1 [ i ] ) / 2

正确性显然

模板


void FWT(int a[],int n)
{
    for(int d=1;d<n;d<<=1)
        for(int m=d<<1,i=0;i<n;i+=m)
            for(int j=0;j<d;j++)
            {
                int x=a[i+j],y=a[i+j+d];
                a[i+j]=(x+y)%mod,a[i+j+d]=(x-y+mod)%mod;
                //xor:a[i+j]=x+y,a[i+j+d]=x-y;
                //and:a[i+j]=x+y;
                //or:a[i+j+d]=x+y;
            }
}

void UFWT(int a[],int n)
{
    for(int d=1;d<n;d<<=1)
        for(int m=d<<1,i=0;i<n;i+=m)
            for(int j=0;j<d;j++)
            {
                int x=a[i+j],y=a[i+j+d];
                a[i+j]=1LL*(x+y)*rev%mod,a[i+j+d]=(1LL*(x-y)*rev%mod+mod)%mod;
                //xor:a[i+j]=(x+y)/2,a[i+j+d]=(x-y)/2;
                //and:a[i+j]=x-y;
                //or:a[i+j+d]=y-x;
            }
}
void solve(int a[],int b[],int n)
{
    FWT(a,n);
    FWT(b,n);
    for(int i=0;i<n;i++) a[i]=1LL*a[i]*b[i]%mod;
    UFWT(a,n);
}

猜你喜欢

转载自blog.csdn.net/qq_35160381/article/details/80217813
FWT