FWT 详解 知识点

BZOJ 同时被 3 个专栏收录
385 篇文章 1 订阅
3 篇文章 0 订阅

前言(扯淡,可以跳过):

其实去年清华集训之后就想写这篇文章了……但是写了一会发现有点说不明白话……于是受限于语文水平一直没有写。前几天给人当面讲了一遍,感觉大概可以BB明白些了……

picks的博客里就写着fwt怎么做,然而他并没有写为什么这样是对的

去年清华集训后的某一天,ljss和我一起刷清华集训题,然后他突然跑来问我一道题:给定数组a,求数组b,使得b[i]=对于所有的j满足j|i==i,sigma a[j]

YY了一会我YY了一个分治做法,然后ljss告诉我这是清华集训D1T2的简化版,然后又告诉我这题是FWT,我想了一会我YY的做法这尼玛不就是FWT的正变换么

然后和ljss一起进行了半天瞎YY,终于YY出了FWT到底是个什么jb玩意


发现我写出了问题还请及时评论-_-

FWT能干什么:

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

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

一个简单的问题:

先考虑一下前言中提到的问题

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

我们考虑按位分治,先把数组长度用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]=对于所有的j满足j&i==i,sigma a[j]的话,那么就是bn0[0]=b0[i]+b1[i],bn1[i]=b1[i]

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

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

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

或卷积、与卷积的原理:

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

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

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

那么如果要求数组a和数组b的与卷积或者或卷积,我们就只需要求出a的正变换a'和b的正变换b',然后另c'[i]=a'[i]*b'[i],那么容易发现c'就是a和b的卷积做正变换的结果

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

逆变换:

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

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

与运算的逆变换同理

然后再递归左右两边即可

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

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

异或卷积:

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

异或卷积基于如下原理:

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

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

证明显然,YY一下即可

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

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

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

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

在正变换的时候,令bn0[i]=b0[i]+b1[i],bn1[i]=b0[i]-b1[i]

在逆变换的时候,令b0[i]=(bn0[i]+bn1[i])/2,b1[i]=(bn0[i]-bn1[i])/2

正确性显然


完结撒花>_M

  • 20
    点赞
  • 13
    评论
  • 5
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值