zoukankan      html  css  js  c++  java
  • O(n log log n)实现FGT和FLT(Fast GCD/LCM Transformation)

    本文是作者看不懂分治FFT之后开始娱乐一下自己写的
    看到一道题时候询问了正解后,推出了一个奇怪的变换,发现这个很Transformation,我和正解推出来的奇怪的东西是一样的,但还是想写一下思路。。。
    (希望原题作者不要来D我啊)

    min卷积和max卷积

    考虑两个卷积
    (C_{min(i,j)} = sum_{i = 1}^{n}sum_{j = 1}^{n} A_{i}B_{j})
    (C_{max(i,j)} = sum_{i = 1}^{n}sum_{j = 1}^{n} A_{i}B_{j})
    普通的卷积,即我们对下标进行加法操作后,将两个数的乘积加到该下标,在这里对下标的操作即使取min和取max

    和暴力卷积一样,这两个卷积是可以暴力(n^2)出解的,但是有没有更优秀的做法呢

    一种变换的出现,往往都是要把卷积变成点积,再用点积反解多项式的系数
    例如FFT是插入函数值,利用两个函数相乘的结果,某个x坐标上的值即对应两个函数值的乘积,再利用单位根的特殊性质将系数解出来
    例如FWT的异或卷积,我们要达成的成就就是(tf(A)* tf(B) = tf(C)),按最高位是0或1来分组,那么就是(C = (A_{0}*B_{0} + A_{1}*B_{1},A_{0} * B_{1} + A_{1} * B_{0})),按照点积等于卷积后的答案,构造两个乘法使得可以得到其中的每一项,即为((A_{0} + A_{1})(B_{0} + B_{1}) = A_{0}B_{0} + A_{0}B_{1} + A_{1}B_{0} + A_{1}B_{1})((A_{0} - A_{1})(B_{0} - B_{1}) = A_{0}B_{0} - A_{1}B_{0} - A_{0}B_{1} + A_{1}B_{1})
    两项相加除二即为前半段,相减除二即为后半段,两个变换即为((tf(A_{0}) + tf(A_{1}),tf(A_{0}) - tf(A_{1})))复杂度(O(n log n))由于(n)往往是(2^{k})所以复杂度会被写成(O(2^{k}k))
    所以我们尝试把这个卷积进行同样的操作
    (然而很多变换的方式都是不知道为啥试出来的)

    对于min卷积
    我们考虑变换(C(i))(SC(i)),(SC(i))表示(x>=i)(C(x))的和,(SA(i))(SB(i))同理
    然后即可发现(SA(i) * SB(i) = SC(i)),最后差分即可还原C数组,复杂度做到了(O(n))

    对于max卷积
    我们考虑变换(C(i))(SC(i)),(SC(i))表示(x<=i)(C(x))的和,(SA(i))(SB(i))同理
    然后即可发现(SA(i) * SB(i) = SC(i)),最后差分即可还原C数组,复杂度做到了(O(n))

    数组的标准分解意义下多维后缀/前缀和

    我们如果把一个数的标准分解(即质因数分解)作为这个数的坐标,例如
    (p_{1}^{k_{1}}p_{2}^{k_2}p_{3}^{k_{3}}...p_{n}^{k_{n}})
    每个数即可看做一个无穷维度里,每一个维度的下标为该质因数的指数的一个坐标

    数组的多维前缀和即
    (f(n) = sum_{d|n}a(d))
    数组的多维后缀和即
    (f(n) = sum_{n|d}a(d))
    朴素的做法可以达到(O(n log n)),可以过很多题目了,即对于一个数枚举它的倍数(用调和级数算复杂度也是一个重要的技巧)

    我们考虑如何将这个算法优化到(O(n log log n))

    我们以前缀和为例,从最小的质因子2开始,固定其他所有质数的指数,开始考虑以下这个过程
    1:a(1)
    2:a(2)
    3:a(3)
    4:a(4)
    5:a(5)
    6:a(6)
    7:a(7)
    8:a(8)
    9:a(9)
    10:a(10)
    初始化即每个数组位置存储对应的a(i)
    第一次操作2,从后往前扫,我们对每个2的倍数进行累加,具体看下面
    1:a(1)
    2:a(2) a(1)
    3:a(3)
    4:a(4) a(2) a(1)
    5:a(5)
    6:a(6) a(3)
    7:a(7)
    8:a(8) a(4) a(2) a(1)
    9:a(9)
    10:a(10) a(5)

    第二次操作3
    1:a(1)
    2:a(2) a(1)
    3:a(3) a(1)
    4:a(4) a(2) a(1)
    5:a(5)
    6:a(6) a(3) a(2) a(1)
    7:a(7)
    8:a(8) a(4) a(2) a(1)
    9:a(9) a(3) a(1)
    10:a(10) a(5)

    第三次操作5
    1:a(1)
    2:a(2) a(1)
    3:a(3) a(1)
    4:a(4) a(2) a(1)
    5:a(5) a(1)
    6:a(6) a(3) a(2) a(1)
    7:a(7)
    8:a(8) a(4) a(2) a(1)
    9:a(9) a(3) a(1)
    10:a(10) a(5) a(2) a(1)

    第四次操作7
    1:a(1)
    2:a(2) a(1)
    3:a(3) a(1)
    4:a(4) a(2) a(1)
    5:a(5) a(1)
    6:a(6) a(3) a(2) a(1)
    7:a(7) a(1)
    8:a(8) a(4) a(2) a(1)
    9:a(9) a(3) a(1)
    10:a(10) a(5) a(2) a(1)

    下一个质数是11,已经超出了范围,所以操作完成了,至此,我们已经完成了数组的多维前缀和

    下面以相同的例子理解数组的多维后缀和
    第一次操作2,从后往前扫
    1:a(1) a(2) a(4) a(8)
    2:a(2) a(4) a(8)
    3:a(3) a(6)
    4:a(4) a(8)
    5:a(5) a(10)
    6:a(6)
    7:a(7)
    8:a(8)
    9:a(9)
    10:a(10)

    第二次操作3
    1:a(1) a(2) a(4) a(8) a(3) a(6) a(9)
    2:a(2) a(4) a(8) a(6)
    3:a(3) a(6) a(9)
    4:a(4) a(8)
    5:a(5) a(10)
    6:a(6)
    7:a(7)
    8:a(8)
    9:a(9)
    10:a(10)

    第三次操作5
    1:a(1) a(2) a(4) a(8) a(3) a(6) a(9) a(5) a(10)
    2:a(2) a(4) a(8) a(6) a(10)
    3:a(3) a(6) a(9)
    4:a(4) a(8)
    5:a(5) a(10)
    6:a(6)
    7:a(7)
    8:a(8)
    9:a(9)
    10:a(10)

    第四次操作7
    1:a(1) a(2) a(4) a(8) a(3) a(6) a(9) a(5) a(10) a(7)
    2:a(2) a(4) a(8) a(6) a(10)
    3:a(3) a(6) a(9)
    4:a(4) a(8)
    5:a(5) a(10)
    6:a(6)
    7:a(7)
    8:a(8)
    9:a(9)
    10:a(10)

    由于对每个质数枚举倍数(根据埃拉托斯特尼筛法的复杂度)是(O(n log log n))
    所以求数组的标准分解意义下的多维前缀和,后缀和可以做到(O(n log log n))

    Fast GCD Transformation 快速最大公约数变换

    由于两个数的GCD,即为两个数标准分解下的每个质因数质数取min,即为
    (p_{1}^{min(a_{1},b_{1}}p_{2}^{min(a_{2},b_{2})}...p_{k}^{min(a_{k},b_{k})})
    类似min卷积,将数组处理成多维后缀和,即为正变换

    考虑如何将变换后的结果还原回去
    sc数组中点积相乘的结果即为
    1:c(1) c(2) c(3) c(4) c(5) c(6) c(7) c(8) c(9) c(10)
    2:c(2) c(4) c(6) c(8) c(10)
    3:c(3) c(6) c(9)
    4:c(4) c(8)
    5:c(5) c(10)
    6:c(6)
    7:c(7)
    8:c(8)
    9:c(9)
    10:c(10)
    考虑如何将他们变换回去,我们发现可以采取我们累加后缀和的步骤反序进行

    即,从大到小枚举质数,从前往后枚举下标

    第一次枚举7
    sc数组中点积相乘的结果即为
    1:c(1) c(2) c(3) c(4) c(5) c(6) c(8) c(9) c(10)
    2:c(2) c(4) c(6) c(8) c(10)
    3:c(3) c(6) c(9)
    4:c(4) c(8)
    5:c(5) c(10)
    6:c(6)
    7:c(7)
    8:c(8)
    9:c(9)
    10:c(10)

    第二次枚举5
    1:c(1) c(2) c(3) c(4) c(6) c(8) c(9)
    2:c(2) c(4) c(6) c(8)
    3:c(3) c(6) c(9)
    4:c(4) c(8)
    5:c(5) c(10)
    6:c(6)
    7:c(7)
    8:c(8)
    9:c(9)
    10:c(10)

    第三次枚举3
    1:c(1) c(2) c(4) c(8)
    2:c(2) c(4) c(8)
    3:c(3) c(6)
    4:c(4) c(8)
    5:c(5) c(10)
    6:c(6)
    7:c(7)
    8:c(8)
    9:c(9)
    10:c(10)

    第四次枚举2
    1:c(1)
    2:c(2)
    3:c(3)
    4:c(4)
    5:c(5)
    6:c(6)
    7:c(7)
    8:c(8)
    9:c(9)
    10:c(10)

    复杂度同样为(O(n log log n)),同时我们可以发现,数组标准分解意义下的多维后缀差分即使前缀和的逆步骤

    同时,如果了解莫比乌斯反演,会列出等式
    (sc(n) = sum_{n|d}c(d))
    (c(n) = sum_{n|d}mu(frac{d}{n})sc(d))
    即,该差分本质上是对(sc(i))进行莫比乌斯反演,我们又得到了一种(O(n log log n))求解莫比乌斯反演的办法,而并非(O(n log n))

    Fast LCM Transformation 快速最小公倍数变换

    我们发现LCM即对两个下标标准分解的每个质因数取max,即
    (p_{1}^{max(a_{1},b_{1})}p_{2}^{max(a_{2},b_{2})}...p_{k}^{max(a_{k},b_{k})})
    但是由于LCM的性质,我们下标的范围会达到(n^{2}),比暴力好不到哪里,所以讨论起来就没有太多意义

    如果我们强制限定长度在N范围内,即变换成数组标准分解的多维前缀和,最后多维前缀差分还原回去,类似FGT

    FGT的代码

    for(int i = 1 ; i <= tot ; ++i) {
        for(int j = N / prime[i] ; j >= 1 ; --j) {
            a[j] += a[j * prime[i]];
            b[j] += b[j * prime[i]]; 
        }    
    }
    for(int i = 1 ; i <= n ; ++i) {
        c[i] = a[i] * b[i];
    }
    for(int i = tot ; i >= 1 ; --i) {
        for(int j = 1 ; j <= N / prime[i] ; ++j) {
            c[j] -= c[j * prime[i]];
        }
    }
    
  • 相关阅读:
    【宗萨仁波切】研究佛教即是研究自己,而研究自己即是发现无我
    【佩玛.丘卓】在当下那一刻觉醒...
    佩玛·丘卓:人生基本的事实
    佩玛.丘卓的生活智慧——【空船】
    佩玛·丘卓 | 宽恕自己,重新开始
    佩玛·丘卓:修炼平等心
    佩玛•丘卓 :我们需要的皆已具足
    【佩玛丘卓】喂养好狼
    佩玛.丘卓:为事情如实的面目而喜悦
    佩玛·丘卓:精神勇士的口诀
  • 原文地址:https://www.cnblogs.com/ivorysi/p/8889154.html
Copyright © 2011-2022 走看看