Algorithm 计算mod 10^9+成对和乘积的另一种方法;7比O快(N^2)

Algorithm 计算mod 10^9+成对和乘积的另一种方法;7比O快(N^2),algorithm,math,Algorithm,Math,给定一个大小为N的整数数组,我想计算 这是过去一次大学间编程竞赛中的一个问题。我们必须编写一个程序来解决这个问题的5个实例,使用N≤200000和每个ai≤200000,在20秒运行时间限制内。显然,O(N2)溶液会超过时间限制。根据研究,预期的解决方案涉及使用快速傅立叶变换的多项式乘法。我正在寻找比没有FFT(或NTT)的朴素O(N2)算法更快的替代算法。这个问题有没有简单而优雅的解决方案 已知事实: mod可以在产品内部“分发”,因为 (x*y)%m=(x%m)*(y%m))%m 更新:

给定一个大小为N的整数数组,我想计算

这是过去一次大学间编程竞赛中的一个问题。我们必须编写一个程序来解决这个问题的5个实例,使用N≤200000和每个ai≤200000,在20秒运行时间限制内。显然,O(N2)溶液会超过时间限制。根据研究,预期的解决方案涉及使用快速傅立叶变换的多项式乘法。我正在寻找比没有FFT(或NTT)的朴素O(N2)算法更快的替代算法。这个问题有没有简单而优雅的解决方案

已知事实:

mod可以在产品内部“分发”,因为 (x*y)%m=(x%m)*(y%m))%m

更新: 以下是竞赛期间的输入/输出测试用例文件:如果它在20秒内通过测试,它将被接受。 输入:
输出:

已经对其进行了更多的教学,帕斯卡三角形是不可行的,因为它会导致更多的操作。幸运的是,mod操作可以移动到PI下,因此您不需要使用大int,而是使用64位算术(或32位modmul)

PI(ai+aj)mod p==PI((ai+aj)mod p)mod p。。。1a[i]+a[j])n'1]);
z> >=1;y=i;
如果((y)和(&(z))x=modmul(x,modpow(y,z,p),p);
}
#ifdef_静态_阵列
#否则
删除[]rle;
删除[]con;
删除[]tmp;
#恩迪夫
返回x;
}
您可以忽略
\u static\u数组
(按未定义的方式处理),这只是为了简化调试。注意卷积
ntt。modmul
不是与任务
p
一起工作,而是与NTTs模一起工作!!!如果您想绝对确定这在更高的
n
或不同的数据分布上有效,请使用64位NTT

下面是关于Edit3方法的讨论:

n=200000
[24527.645 ms] 863132560 O(m^2) RLE(a[i]) -> RLE(a[i]+a[j]) m <= n
[  754.409 ms] 863132560 O(m.log(m)) RLE(a[i])+NTT

可悲的是,Karatsuba的开销太大了,所以阈值超过了
n=200000
,这使得它对这项任务毫无用处。

既然
ai,为什么你认为O(n^2)会超过时限。要更详细地解释inteded解决方案,这里是完整社论的链接:(这个问题在最后一页)。@EdHeal Well,根据我在编程竞赛中的经验,自动评委可以在1秒内完成10^6个操作。O(N^2)算法大约需要200000^2=4*10^10次运算。此外,社论说,它将暂停使用一种朴素的O(N^2)算法。除了基于FFT的算法之外,唯一的次二次型替代方法是Karatsuba/Toom-Cook方法。但是那些在多项式时间内运行且不是
O(N log(N))
@Spektre多项式乘法的函数具有
(求和系数输出)=(求和系数输入A)*(求和系数输入B)
的性质,因为系数之和是
N
,所以输出系数之和是
N^2
。由于
N<200000
N^2<2^36
。因此,最大系数为<
2^36
,该系数非常适合于双精度
的范围,并且没有发生FFT舍入误差的风险。或者,如果你非常害怕FFT,你可以在一个合适的模上做一个NTT。我也这么想,但有一个问题,它是三角形而不是正方形,所以有20.000.000.000个热,没有代数副本。因此,只会出现数字副本,这意味着您需要检查所有
(n^2)/2
therms,并且只有不做任何操作的虚拟嵌套for循环将花费太多时间(
n=100000
在我的设置中几乎花费了3秒),因此如果您添加任何搜索和计算或表创建,情况会更糟。。。这就是为什么我从一开始就放弃了这种方法:(但这仍然是一个好主意。)+1@Spektre当前位置不要挂在三角形上。找到术语(ai+ai)是O(N),形成对角线,非对角线项ai+aj的重数是平方的一半,因为ai+aj==aj+ai。我想我没有得到素数/因式分解角度。但是,如果有一种快速的方法得到重数,那么将40万次快速幂次的结果相乘听起来远没有40E12次乘法那么可怕es-你在没有提到计数排序的情况下就画了一个,这比我强。@greybeard那么可能是我在测量空
O(n^2)时遗漏了一些东西
for循环已经在超时范围内,没有读取数组之类的功能。我认为这种方法需要遍历所有的数组,或者我遗漏了什么?如果数组被排序,那么我可以看到这是一种方式,但除此之外…@greybeard:更好的是,你只需要处理40000个素数。factorzatio通过查找n是O(1)。
n=10000
[ 749.033 ms] 149252794 O(n^2)        naive
[1077.618 ms] 149252794 O(n'^2)       RLE(a[i])+fast_sqr32
[ 568.510 ms] 149252794 O(n'^1.585)   RLE(a[i])+Karatsuba32
[  65.805 ms] 149252794 O(n'^2)       RLE(a[i]) -> RLE(a[i]+a[j])
[  53.833 ms] 149252794 O(n'.log(n')) RLE(a[i])+FFT
[  34.129 ms] 149252794 O(n'.log(n')) RLE(a[i])+NTT
n=20000
[3084.546 ms] 365847531 O(n^2)        naive
[4311.491 ms] 365847531 O(n'^2)       RLE(a[i])+fast_sqr32
[1672.769 ms] 365847531 O(n'^1.585)   RLE(a[i])+Karatsuba32
[ 238.725 ms] 365847531 O(n'^2)       RLE(a[i]) -> RLE(a[i]+a[j])
[ 115.047 ms] 365847531 O(n'.log(n')) RLE(a[i])+FFT
[  71.587 ms] 365847531 O(n'.log(n')) RLE(a[i])+NTT
n=40000
[12592.250 ms] 347013745 O(n^2)        naive
[17135.248 ms] 347013745 O(n'^2)       RLE(a[i])+fast_sqr32
[5172.836 ms] 347013745 O(n'^1.585)   RLE(a[i])+Karatsuba32
[ 951.256 ms] 347013745 O(n'^2)       RLE(a[i]) -> RLE(a[i]+a[j])
[ 242.918 ms] 347013745 O(n'.log(n')) RLE(a[i])+FFT
[ 152.553 ms] 347013745 O(n'.log(n')) RLE(a[i])+NTT