zoukankan      html  css  js  c++  java
  • 单位根与其若干应用

    单位根与其若干应用

    单位根的几个性质

    对于一种运算条件下,若((w_n^1)^n = 1),那么(w_n^1)(n)次单位根。
    我们令(w_{n}^k = (w_{n}^1)^k)
    单位根在复数意义下有三角表示:(w_n^k = e^{frac{2pi k}{n}i} = cos(frac{2pi k}{n}) + sin(frac{2pi k}{n})i)
    在模(p)意义下,由于原根(g)满足(g^{p-1} equiv 1(mod p)),所以可以令(w_{p}^1 = g^{frac{p-1}{n}})
    复数运算的几何意义:模长相乘,转角相加。
    根据复数乘法的几何意义,显然有:

    • (w_{n}^k = w_{nr}^{kr})
    • (w_{n}^k = w_{n}^{k\% n})
    • (w_{n}^{k + frac{n}{2}} = -w_{n}^k)

    这几个性质后面都会用到。

    单位根反演

    我们有式子:(frac{1}{n}sum_{i=0}^{n - 1} (w_{n}^k)^i = [n|k]),证明其实不难。
    (n|k),那么(w_n^k = w_{n}^0 = 1),所以(sum_{i=0}^{n-1} (w_n^k)^i = n)
    否则,(sum_{i=0}^{n-1} (w_n^k)^i = frac{1-(w_{n}^{k})^n}{1 - w_{n}^k} = frac{1 - w_n^0}{1 - w_n^k} = 0)
    证毕。
    可以化简大量包含形如(sum_i[d|i] F(i))的式子的计算。

    离散傅里叶变换(DFT)

    我们用(n)个点可以表示出(n-1)次多项式,称这种表示为点值表示法。
    点值表示法的多项式乘法显然为(O(n)),即((x,y_1)*(x,y_2) = (x,y_1y_2))
    点值表示法转系数表示法的传统做法为拉格朗日差值,复杂度(O(n^2))
    傅里叶提出,通过带入单位根,优化这个过程。
    对于一个(n-1)次多项式(A(x) = sum_{i=0}^{n-1} a_i x^i),考虑带入(x_k = w_{n}^k)来做出点值表示,其中(kin[0,n))
    我们设得到的点值表示为(A(x):{(w_n^k,y_k)|kin [0,n)}) ,令(B(x) = sum_{i=0}^{n-1} y_i x^i)
    (w_{n}^{k})的倒数分别带入(B(x))中:
    (B(w_n^{-k}) = sum_{i=0}^{n-1} y_i w_n^{-ki} = sum_{i=0}^{n-1} (sum_{j=0}^{n-1} a_j w_n^{ij})w_n^{-ki})
    (B(w_n^{-k}) = sum_{j=0}^{n-1} sum_{i=0}^{n-1} a_j w_n^{(j-k)i}),后面一坨就是单位根反演的变形。
    (B(w_n^{-k}) = sum_{j=0}^{n-1} a_j (sum_{i=0}^{n-1} w_n^{(j-k)i}) = k_j n)
    所以(a_k = frac{B(w_n^{-k})}{n}),爽!
    可以发现我们不需要拉格朗日差值了,带入单位根倒数得点值表示,然后除(n)就是每一项的系数了。
    总结一下,我们得到了这样的东西:
    (f_i = sum_{j=0}^{n-1} w_n^j g_j) --------------------> (g_i = frac{1}{n}sum_{j=0}^{n-1} w_{n}^{-j} f_j)

    快速傅里叶变换(FFT)

    考虑利用离散傅里叶变换,实现快速多项式乘法。
    我们假设求长度为(n)的多项式的离散傅里叶变换,设(d|n)
    考虑求(A(x) = sum_{i=0}^{n-1} a_i x^i)带入(w_n^{0,1...n-1})的点值表示结果,我们按照模(d)剩余类分类。
    (A_r(x) = sum_{i=0}^{frac{n}{d} - 1} a_{id+ r} x^i),假设我们已经求得了它们带入(w_{frac{n}{d}}^{0,1,...frac{n}{d}-1})的点值表示结果。
    那么(A(w_{n}^k) = sum_{r=0}^{d-1} (w_{n}^{kr}) A_r((w_{n}^k)^d) = sum_{r=0}^{d-1} (w_n^{kr\% n}) A_r(w_{frac{n}{d}}^k))
    所以貌似分治就行了......
    一般使用时,我们把(n)补全为(2^t),这样(d)恒等于(2),每次折半分治即可。
    关于非递归(FFT)
    预处理每个点分治若干次后到达的实际位置,然后从底向上还原(每次合并都是一段区间)。
    快速数论变换(NTT)的原理与快速傅里叶变换(FFT)没有任何区别,所以就不写了。

    循环卷积

    当多项式乘法在指数模(n)意义进行,我们乘其为指数模(n)意义下的循环卷积。
    而单位根有很好的性质:(w_{n}^k = w_{n}^{k\% n})
    我们假设能够求出带入(w_n^{0,1,2...,n-1})后多项式的值,那么(Idft)就能够求出原多项式的每一项系数。
    一类特殊循环卷积:
    考虑对于长度为(n)的多项式(A(x)),(B(x)),求:
    (C(x): C_k = sum_{i=0}^{n-1} sum_{j=0}^{n-1}[(i+j)\%n=k] a_ib_j)
    一个非常有趣的结论:
    求得(A(x))(B(x))的离散傅里叶变换的点值,那么(C(x))的点值表示就为(A(x))点值与(B(x))点值的积。
    即若你要求(A(x)^m),那么只需要求(A(x))的傅里叶变换点值,把点值(m)次方后再还原回去即可。
    所谓傅里叶变换点值就是指带入(w_{n}^{0,1...,n - 1})得到的点值 。
    我们来证明一下,其实暴力带入验证即可。
    (C(w_n^k) = A(w_n^k) B(w_n^k) = (sum_{i=0}^{n-1} a_i w_n^{ki})(sum_{j=0}^{n-1} b_j w_{n}^{kj}))
    (C(w_n^k) = sum_{i=0}^{n-1} sum_{j=0}^{n-1} a_ib_j w^{k(i+j)}_n = sum_{r=0}^{n-1} sum_{i=0}^{n-1} sum_{j=0}^{n-1}[(i+j)\%n=r] a_ib_j w_n^{k})
    刚好就是循环卷积要求的东西,证毕。

    几道有趣的题目

    嘿嘿嘿......见下面。

    [CTSC2010] 性能优化

    问题就是求(B(x)^C)在指数模(n)意义下的循环卷积,同时模数为(n+1),多项式长度为(n)
    我们在上面已经讨论过这种循环卷积的处理方式了。
    所以只需要求(B(x))带入(w_{n}^{0,1...n-1})的点值表示,然后(C)次方后再(Idft)回去。
    我们需要支持快速(dft),而我们已知(n)能够分解为(2^{k_1}3^{k_2}5^{k_3} 7^{k_4})
    那么考虑分治(dft),类似(fft)每次把多项式分成若干份,然后自底向上合并即可。
    由于每次分成的份数不超过(7),所以合并的复杂度显然是正确的。
    可以预先搜出每个点在分治(dft)的过程中最后到了哪个位置,这样就可以非递归实现算法了。

    [牛客挑战赛23F] 计数(解法一)

    对于一棵生成树,设其边权为(sum_{ein E} val_e),那么(Ans = sum_E [k|sum_{ein E} val_e])
    单位根反演有:(Ans = frac{1}{k}sum_E sum_{j=0}^{k-1} (w_k^{sum_{ein E} val_e})^j = frac{1}{k} sum_{j=0}^{k-1} sum_E prod_{ein E} (w_k^j)^{val_e})
    后面一坨是我们熟悉的变元矩阵树定理,所以(sum_E prod_{ein E} (w_k^j)^{val_e})直接用矩阵树定理算即可。

    [牛客挑战赛23F] 计数(解法二)

    依旧考虑变元矩阵树定理,考虑把边权相加变为边权相乘。
    定义每条边(e)的生成函数((1 + x^{val_e})),那么一个合法边集可以看做在指数模(k)意义下的循环卷积。
    考虑令(x = w_{k}^{0,1...k-1}),那么带入(w_k^j)做矩阵树定理就能够得到(A(w_k^{j}))
    而我们的最终目标是求(A(x))中的系数(a_0),所以再(Idft)即可。

    [牛客挑战赛11E] 白兔的刁难(解法一)

    考虑组合意义,不难发现(ans_t)就是((1+x)^n)在指数模(k)意义下的循环卷积第(t)项的系数。
    暴力带入(w_k^{0,1...,k-1}),得点值表示后再(Idft)即可。

    [牛客挑战赛11E] 白兔的刁难(解法二)

    考虑暴力单位根反演,(Ans_t = sum_{i=0}^{n} inom{n}{i} [k|i-t] = frac{1}{k}sum_{i=0}^{n} inom{n}{i} sum_{j=0}^{k-1} (w_{k}^{i-t})^j)
    所以(Ans_t = frac{1}{k} sum_{j=0}^{k-1} (w_k^{-t})^j sum_{i=0}^n inom{n}{i} (w_{k}^i)^j = frac{1}{k} sum_{j=0}^{k-1} (w_k^{-t})^j (1 + w_k^j)^n)
    不难发现这是一个(Idft)形式。
    我们把((1+w_k^j)^n)看做(A(w_k^j))(指数模(k)意义下的循环卷积),即((1+w_k^j)^n = A(w_k^j) = y_j)
    那么(Ans_t = B(w_k^{-t}) = frac{1}{k} sum_{j=0}^{k-1} y_j (w_k^{-t}) = frac{1}{k} (a_t k) = a_t)
    所以(Ans_t)等于(A(x) = (1 + x)^n)这个循环卷积的第(t)项系数,与我们组合意义所得的内容一致。

  • 相关阅读:
    div标签的闭合检查
    jquery easyui 显示和关闭数据加载的遮罩
    codeforces 446A DZY Loves Sequences
    android高速开发框架xUtils
    Android-spinner
    遗传算法优化策略
    面向对象的勾勾画画
    Android studio 解决setText中文乱码问题
    CAS—改动默认登录页
    android 使用post 提交
  • 原文地址:https://www.cnblogs.com/GuessYCB/p/10324928.html
Copyright © 2011-2022 走看看