有dalao已经贴出来思路和代码了
本鶸看的不太懂,于是又自己百度了百度,学了学,把dalao博客没有细说的部分给详细的讲了讲
当然快速傅里叶变换我也讲的不好,完全不了解的可以去看一看b站上的这个视频
代码我就不写了,是按照之前dalao的代码写的。(其实就是写的太烂了)
思路
我们可以发现
的充分必要条件是
可以证明:
不妨假设
显然有 :
可以化为:
那么
等价于
所以,只要
,那么
以上证明逐步可逆
所以我们的任务就变为了求出所有的
,那么如何求出它呢?这时,我们就可以利用离散卷积。
考虑一下卷积的计算过程,假如有序列
,则卷积其卷积为
可以看出,
的值是序列
的所有 *自变量之和为n时,它们的函数值的乘积 *之和
如果我们把给出的自然数映射到新的数组p,
表示数字
存在,
等于零表示数字
不存在。
我们先来考虑一个类似的问题,假如给定了a,b两个自然数数组,怎么知道a,b两个数组中任意两个数相加后所得到的新数组中都有哪些数呢?
前文说到,
的值是序列
的所有 *自变量之和为n时,它们的函数值的乘积 *之和
那么我们分别把a, b两个数组转换为pa, pb数组,对它们进行卷积,得到了数组P
那么我们想一下数组P的每一个数是怎么求出来了,对于
,它的值为
其中,
,而
的值只有0,1两中情况,那么 **只有至少有一组
均为1时,
的值才不为0。**
所以,卷积得到的P数组,其实就是我们需要的结果,只需检查一下P数组的每一个,只要当前下标下的值大于0,那么就意味着这个数存在于新的数组。
那么回到我们现在的问题,我们知道了加法,那么我们如何解决当前这个问题呢?
其实很简单,我们把数组原来的所有数组取相反数,再映射到新的p数组,得到p‘(当然此时的p’数组下标均为负数),那么我们只需要把p和p‘进行卷积,得到的P数组,就是我们要的结果了。
当然因为C++中不能使用负数作为下标,所以我们要做数组平移,即把下标 -x 改为 Max - x,就可以了。
如果我们按照定义的方法去求卷积,其复杂度为
,是和暴力算法复杂度一样。
一直以来的努力全部木大,但是,离散卷积,刚好就是快速傅里叶变换(FFT)解决多项式相乘问题中的一步。所以我们可以使用FFT来优化离散卷积过程,把时间复杂度降为。
题外话
一个n阶多项式系数数组经过FFT后得到的数组F,
就是
阶单位根为自变量的值时,多项式的值。
得到了所有的
数组,我们只需要找一个不被它们整除的数即可,时间复杂度
。