拆系数FFT

对于任意模数 \(mod\)

设\(m=\sqrt {mod}\)

把多项式\(A(x)\)和\(B(x)\)的系数都拆成\(a\times m+b\)的形式,时\(a, b\)都小于\(m\)

提出,那么一个多项式就可以拆成两个多项式的加法

一个是\(a*m\)的,一个是\(b\)的

直接乘法分配律,\(aa\)一遍,\(ab\)一遍,\(ba\),\(bb\)一遍,四遍\(FFT\)

乘出来不会超过取模范围

然后合并直接

\[(a\times m+b)(c\times m+d)=a\times c\times m^2+(a\times c+b\times d)m+b\times d
\]

这样子要进行 \(7\) 次 \(DFT\)

如果研究一下 \(myy\) \(2016\) 年的集训队论文就会发现有 \(2\) 次 或者 \(1.5\) 次 \(DFT\) 的 \(FFT\) 算法

2次的够了吧

\(myy\) 巧妙的运用了复数的虚部,优化了算法

具体来说

\[C(x)=A(x)+iB(x)
\]

\[D(x)=A(x)-iB(x)
\]

假设

\(c(w_n^k)\) 表示将 \(C(x)\) \(DFT\) 后的点值

\(d(w_n^k)\) 表示将 \(D(x)\) \(DFT\) 后的点值

\(w\) 为 \(n\) 次单位复数根

设 \(conj(x)\) 表示 \(x\) 的共轭复数

那么

\[c(w_{2n}^{k})=A(w_{2n}^{k})+iB(w_{2n}^{k})=\sum_{j=0}^{2n-1}A_jw_{2n}^{jk}+iB_jw_{2n}^{jk}
\]

\[=\sum_{j=0}^{2n-1}(A_j+iB_j)(cos\frac{\pi jk}{n}+isin \frac{\pi jk}{n})
\]

\[d(w_{2n}^{k})=A(w_{2n}^{k})-iB(w_{2n}^{k})=\sum_{j=0}^{2n-1}(A_j-iB_j)(cos\frac{\pi jk}{n}+isin \frac{\pi jk}{n})
\]

设 \(x=\frac{\pi jk}{n}\)

那么

\[d(w_{2n}^{k})=\sum_{j=0}^{2n-1}(A_jcosx+B_jsinx)+i(A_jsinx-B_jcosx)
\]

\[=conj(\sum_{j=0}^{2n-1}(A_jcosx+B_jsinx)-i(A_jsinx-B_jcosx))
\]

\[=conj(\sum_{j=0}^{2n-1}(A_jcos(-x)-B_jsin(-x))+i(A_jsin(-x)+B_jcos(-x)))
\]

\[=conj(\sum_{j=0}^{2n-1}(A_j+iB_j)(cos(-x)+isin(-x)))
\]

\[=conj(\sum_{j=0}^{2n-1}(A_j+iB_j)(cos(2\pi-x)+isin(2\pi-x)))
\]

\[=conj(c(w_{2n}^{2n-k}))
\]

也就是说

只需要一次 \(DFT\) 求出 \(c\) 就可以求出 \(d\)

那么

\[DFT(A(k))=\frac{c(w^{k})+d(w^{k})}{2}
\]

\[DFT(B(k))=\frac{c(w^{k})-d(w^{k})}{2i}
\]

再用一次 \(DFT^{-1}\) 还原出多项式,多项式乘法只要两次 \(DFT\)

考虑拆系数 \(FFT\)

\(DFT\) 两两合并,从 \(4\) 次变成 \(2\) 次

\(DFT^{-1}\) 合并其中一个,从 \(3\) 次变成 \(2\) 次

一共 \(4\) 次

# include <bits/stdc++.h>
using namespace std;
typedef long long ll; namespace IO {
const int maxn((1 << 21) + 1); char ibuf[maxn], *iS, *iT, obuf[maxn], *oS = obuf, *oT = obuf + maxn - 1, c, st[65];
int f, tp; char Getc() {
return (iS == iT ? (iT = (iS = ibuf) + fread(ibuf, 1, maxn, stdin), (iS == iT ? EOF : *iS++)) : *iS++);
} void Flush() {
fwrite(obuf, 1, oS - obuf, stdout);
oS = obuf;
} void Putc(char x) {
*oS++ = x;
if (oS == oT) Flush();
} template <class Int> void In(Int &x) {
for (f = 1, c = Getc(); c < '0' || c > '9'; c = Getc()) f = c == '-' ? -1 : 1;
for (x = 0; c <= '9' && c >= '0'; c = Getc()) x = (x << 3) + (x << 1) + (c ^ 48);
x *= f;
} template <class Int> void Out(Int x) {
if (!x) Putc('0');
if (x < 0) Putc('-'), x = -x;
while (x) st[++tp] = x % 10 + '0', x /= 10;
while (tp) Putc(st[tp--]);
}
} using IO :: In;
using IO :: Out;
using IO :: Putc;
using IO :: Flush; const int maxn(1 << 18);
const double pi(acos(-1)); struct Complex {
double a, b; inline Complex() {
a = b = 0;
} inline Complex(double _a, double _b) {
a = _a, b = _b;
} inline Complex operator +(Complex x) const {
return Complex(a + x.a, b + x.b);
} inline Complex operator -(Complex x) const {
return Complex(a - x.a, b - x.b);
} inline Complex operator *(Complex x) const {
return Complex(a * x.a - b * x.b, a * x.b + b * x.a);
} inline Complex Conj() {
return Complex(a, -b);
}
}; Complex a[maxn], b[maxn], w[maxn], a1[maxn], a2[maxn];
int r[maxn], l, deg, g[maxn], h[maxn], mod; inline void FFT(Complex *p, int opt) {
register int i, j, k, t;
register Complex wn, x, y;
for (i = 0; i < deg; ++i) if (r[i] < i) swap(p[r[i]], p[i]);
for (i = 1; i < deg; i <<= 1)
for(t = i << 1, j = 0; j < deg; j += t)
for (k = 0; k < i; ++k) {
wn = w[deg / i * k];
if (opt == -1) wn.b *= -1;
x = p[j + k], y = wn * p[i + j + k];
p[j + k] = x + y, p[i + j + k] = x - y;
}
} inline void Mul(int n, int *p, int *q, int *f) {
register int i, k, v1, v2, v3;
register Complex ca, cb, da1, da2, db1, db2;
for (deg = 1, l = 0; deg < n; deg <<= 1) ++l;
for (i = 0; i < deg; ++i) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (l - 1));
for (i = 0; i < deg; ++i) w[i] = Complex(cos(pi * i / deg), sin(pi * i / deg));
for (i = 0; i < n; ++i) a[i] = Complex(p[i] & 32767, p[i] >> 15), b[i] = Complex(q[i] & 32767, q[i] >> 15);
for (FFT(a, 1), FFT(b, 1), i = 0; i < deg; ++i) {
k = (deg - i) & (deg - 1), ca = a[k].Conj(), cb = b[k].Conj();
da1 = (ca + a[i]) * Complex(0.5, 0), da2 = (a[i] - ca) * Complex(0, -0.5);
db1 = (cb + b[i]) * Complex(0.5, 0), db2 = (b[i] - cb) * Complex(0, -0.5);
a1[i] = da1 * db1 + (da1 * db2 + da2 * db1) * Complex(0, 1), a2[i] = da2 * db2;
}
for (FFT(a1, -1), FFT(a2, -1), i = 0; i < deg; ++i) {
v1 = (ll)(a1[i].a / deg + 0.5) % mod, v2 = (ll)(a1[i].b / deg + 0.5) % mod;
v3 = (ll)(a2[i].a / deg + 0.5) % mod, f[i] = (((ll)v3 << 30) + ((ll)v2 << 15) + v1) % mod;
if (f[i] < 0) f[i] += mod;
}
} int main() {
register int len, i, n, m;
In(n), In(m), In(mod), ++n, ++m;
for (i = 0; i < n; ++i) In(h[i]), h[i] %= mod;
for (i = 0; i < m; ++i) In(g[i]), g[i] %= mod;
for (len = 1, n += m - 1; len < n; len <<= 1);
for (Mul(len, h, g, g), i = 0; i < n; ++i) Out(g[i]), Putc(' ');
return Flush(), 0;
}

拆系数FFT(任意模数FFT)的更多相关文章

  1. 任意模数FFT

    任意模数FFT 这是一个神奇的魔法,但是和往常一样,在这之前,先 \(\texttt{orz}\ \color{orange}{\texttt{matthew99}}\) 问题描述 给定 2 个多项式 ...

  2. 【集训队作业2018】取名字太难了 任意模数FFT

    题目大意 求多项式 \(\prod_{i=1}^n(x+i)\) 的系数在模 \(p\) 意义下的分布,对 \(998244353\) 取模. \(p\) 为质数. \(n\leq {10}^{18} ...

  3. 51nod 1172 Partial Sums V2 卡精度的任意模数FFT

    卡精度的任意模数fft模板题……这道题随便写个表就能看出规律来(或者说考虑一下实际意义),反正拿到这题之后,很快就会发现他是任意模数fft模板题.然后我就去网上抄了一下板子……我打的是最土的任意模数f ...

  4. 51NOD 1258 序列求和 V4 [任意模数fft 多项式求逆元 伯努利数]

    1258 序列求和 V4 题意:求\(S_m(n) = \sum_{i=1}^n i^m \mod 10^9+7\),多组数据,\(T \le 500, n \le 10^{18}, k \le 50 ...

  5. hdu 4656 Evaluation [任意模数fft trick]

    hdu 4656 Evaluation 题意:给出\(n,b,c,d,f(x) = \sum_{i=1}^{n-1} a_ix^i\),求\(f(b\cdot c^{2k}+d):0\le k < ...

  6. 洛谷 4245 【模板】任意模数NTT——三模数NTT / 拆系数FFT

    题目:https://www.luogu.org/problemnew/show/P4245 三模数NTT: 大概是用3个模数分别做一遍,用中国剩余定理合并. 前两个合并起来变成一个 long lon ...

  7. 洛谷 P4245 [模板]任意模数NTT —— 三模数NTT / 拆系数FFT(MTT)

    题目:https://www.luogu.org/problemnew/show/P4245 用三模数NTT做,需要注意时间和细节: 注意各种地方要取模!传入 upt() 里面的数一定要不超过2倍 m ...

  8. 拆系数FFT

    学习内容:国家集训队2016论文 - 再谈快速傅里叶变换 模板题:http://uoj.ac/problem/34 1.基本介绍 对长度为L的\(A(x),B(x)\)进行DFT,可以利用 \[ \b ...

  9. 拆系数FFT及其部分优化

    模拟考某题一开始由于校内OJ太慢直接拆系数FFT跑不过 后来被神仙婊了一顿之后发现复杂度写炸了改了改随便过 模版题:任意模数NTT 三模数NTT 常数巨大,跑的极慢 拆系数FFT 原理是对于两个多项式 ...

随机推荐

  1. 高可用群集HA介绍与LVS+keepalived高可用群集

    一.Keepalived介绍 通常使用keepalived技术配合LVS对director和存储进行双机热备,防止单点故障,keepalived专为LVS和HA设计的一款健康检查工具,但演变为后来不仅 ...

  2. (二)Python 装饰器

    1. 函数 在 Python 中,使用关键字 def 和一个函数名以及一个可选的参数列表来定义函数.函数使用 return 关键字来返回值.定义和使用一个最简单的函数例子: >>> ...

  3. markdown页面内跳转

    分两步 第一步 实际语法比较简单,在需要跳转的位置添加锚点,语法如下: <span id="jump">跳转到的地方</span> 第二步 在需要点击跳转的 ...

  4. Mac下常用按键符号⌘(command)、⌥(option)、⇧(shift)、⇪(caps lock)、⌃(control)、↩(return)、⌅(enter)

    常用符号如下: ⌘(command) ⌥(option) ⇧(shift) ⇪(caps lock) ⌃(control) ↩(return) ⌅(enter) 对应键盘的位置如下: 如果每次都不记得 ...

  5. Java 8学习之Stream API

    一个Stream表面上看与一个集合很类似,允许你改变和获取数据.但是实际上他与集合是有很大区别的: Stream自己不会存储元素.元素可能被存储在底层的集合中,或者根据需要产生出来. Stream操作 ...

  6. 《LeetBook》leetcode题解(3):Longest Substring Without Repeating Characters[M]——哈希判断重复

    我现在在做一个叫<leetbook>的免费开源书项目,力求提供最易懂的中文思路,目前把解题思路都同步更新到gitbook上了,需要的同学可以去看看 书的地址:https://hk029.g ...

  7. Scala IDEA for Eclipse里用maven来创建scala和java项目代码环境(图文详解)

    这篇博客 是在Scala IDEA for Eclipse里手动创建scala代码编写环境. Scala IDE for Eclipse的下载.安装和WordCount的初步使用(本地模式和集群模式) ...

  8. Java二进制兼容性原理

    一.概述 现在的软件越来越依赖于不同厂商.作者开发的共享组件,组件管理也变得越来越重要.在这方面,一个极其重要的问题是类的不同版本的二进制兼容性,即一个类改变时,新版的类是否可以直接替换原来的类,却不 ...

  9. JavaScript数据结构-13.散列碰撞(开链法)

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  10. 一头扎进 Java IO中-------java IO文件

    Java IO: 文件 在Java应用程序中,文件是一种常用的数据源或者存储数据的媒介.所以这一小节将会对Java中文件的使用做一个简短的概述.这篇文章不会对每一个技术细节都做出解释,而是会针对文件存 ...