多项式整合包
update:
学习多项式!!
1.快速傅里叶变换(FFT)
当然是从
1.1 DFT & IDFT
1.1.1 系数与点值
系数表达:
这样我们求
直接算的复杂度显然是
然后我们要知道: n+1个点值(有序数对)可以来确定一个n次多项式
所以多项式的 点值表达 就是用
点值表达:
我们利用这样的表示法可以快速的求出两个 多项式的乘法。
只需将
但是
我们直接找
这样我们就完成了
复杂度仅需
1.1.2 FFT的流程
上述我们可以知道在多项式乘法中 点值表达 的复杂度很优,但是我们一般的多项式都是系数表达 ,我们如何实现 点值 与 系数 之间的转换呢?: )
“把系数表达转换为点值表达”的算法叫做 DFT
“把点值表达转换为系数表达”的算法叫做 IDFT(DFT的逆运算)
传承老图:
- 从一个多项式的系数表达确定其点值表达的过程称为求值
- 而求值运算的逆运算(也就是从一个多项式的点值表达确定其系数表达)被称为插值.
1.2 单位根的性质
这一点讲如何利用一些有神奇性质的
复数 不讲。
n次单位根: 方程
几何意义:将单位圆均分为
复数表示:
单位根的性质:
,根据几何或者直接表示为复数易证。 ,几何上相当于绕了一个圆周 ,几何上相当于转了半个圆周
这些性质有大用
1.3 单位根的巧用
一个多项式
我们把它拆成平均的两份:
则可以得到
这样就可以分治了!!
我们把神奇的单位根带入进去:
代入
代入
两式的差别只是正负号。
有什么用呢?可以快速求值,我们利用
最终得出来了
至于IDFT。
结论:只需带入
1.4 FFT 的实现
上面听不懂没关系,背代码就行了:(
我们上面的方法是递归分治的,会造成大量数组拷贝,是不优的。
有一种方法可以避免,就是迭代FFT(即从小合并)。
但这样我们需要找出每个点最后所在的位置,结论就是原数字的二进制翻转(很奇怪),可以
for(int i = 1;i < k;i++)rev[i] = (rev[i>>1]>>1) | ((i&1)<<(b-1));
剩下的直接看代码吧:)
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define db double
const int N = 2e6+1e5;//2^20 = 1048576
const db pi = acos(-1.0);
inline int read(){
int x = 0,f = 1;char c = getchar();
for(;c < '0' || c > '9';c = getchar())if(c == '-')f = -1;
for(;c >= '0' && c <= '9';c = getchar())x = (x<<1) + (x<<3) + c-'0';
return x * f;
}
struct cp{
db x,y;
cp(){x = y = 0;}
cp(db x,db y):x(x),y(y){}
inline cp operator + (const cp a)const{return cp(a.x+x,a.y+y);}
inline cp operator - (const cp a)const{return cp(x-a.x,y-a.y);}
inline cp operator * (const cp a)const{return cp(x*a.x-y*a.y,x*a.y+y*a.x);}
}F[N],G[N];
int n,m;
int rev[N],b;
void FFT(int n,cp *a,int op){
for(int i = 0;i < n;i++)if(i < rev[i])swap(a[i],a[rev[i]]);
for(int len = 1;len <= (n>>1);len <<= 1){
cp w1(cos(pi/len),sin(pi/len)*op);
for(int i = 0;i <= n - (len<<1);i += (len<<1)){
cp w(1,0);
for(int j = i;j < i+len;j++){
cp x = a[j],y = w*a[j+len];
a[j] = x+y,a[j+len] = x-y;
w = w * w1;
}
}
}
}
int main(){
n = read(),m = read();
for(int i = 0;i <= n;i++)F[i].x = read();
for(int i = 0;i <= m;i++)G[i].x = read();
int k = 1;
while(k <= n+m)k <<= 1,b++;
for(int i = 1;i < k;i++)rev[i] = (rev[i>>1]>>1) | ((i&1)<<(b-1));
FFT(k,F,1),FFT(k,G,1);
for(int i = 0;i <= k;i++)F[i] = F[i] * G[i];
FFT(k,F,-1);
for(int i = 0;i <= n+m;i++)printf("%d ",(int)(F[i].x/k+0.5));
return 0;
}
全文背默!!!
1.5 其他优化
咕咕咕
1.6 快速数论变换(NTT)
学原根回来哩:)
因为FFT引用了复数单位根,所以精度要求很高,跑的可能会慢。
所以我们需要NTT。
1.6.1 阶与原根
详见数论学习笔记1.3
我就不搬了。
1.6.2 原根类似的性质
(以下
由原根性质2可得
我们令
所以我们有以下性质:
第四条证明一下:
- 由费马小定理知
,即 ,即 ,又因为 互不相同且 ,得出 ,即得证。
这样原根与单位根的性质就完全相同了!!: )
我们只需要找到一个模数
一般NTT模数:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define F(i,a,b) for(int i = a;i <= b;i++)
#define D(i,a,b) for(int i = a;i >= b;i--)
#define in inline
#define pb(x) push_back(x)
#define db double
const db pi = acos(-1);
const int N = 2e6+1e5,mod = 998244353,g = 3;
ll read(){
ll x = 0,f = 1;char c = getchar();
for(;c < '0' || c > '9';c = getchar())if(c == '-')f = -1;
for(;c >= '0' && c <= '9';c = getchar())x = (x<<3) + (x<<1) + c-'0';
return x * f;
}
int ksm(int x,int y = mod-2){
int ans = 1;
while(y){
if(y & 1)ans = 1ll * ans * x % mod;
x = 1ll * x * x % mod,y >>= 1;
}return ans;
}
int b,n,m;
int rev[N],invg = ksm(g),invn;
ll F[N],G[N];
void NTT(int n,ll *a,int op){
F(i,0,n)if(i < rev[i])swap(a[i],a[rev[i]]);
for(int len = 1;len <= (n>>1);len <<= 1){
ll tg = ksm(op?g:invg,(mod-1)/(len<<1));
for(int i = 0;i + (len<<1) <= n;i += (len<<1)){
ll gg = 1;
for(int j = i;j < i + len;j++){
ll x = a[j],y = 1ll * gg * a[j+len] % mod;
a[j] = (x + y) % mod,a[j+len] = (x - y + mod) % mod;
gg = 1ll * gg * tg % mod;
}
}
}
}
int main(){
n = read(),m = read();
F(i,0,n)F[i] = read();
F(i,0,m)G[i] = read();
int t = 1;
while(t <= n+m)t <<= 1,b++;
F(i,1,t)rev[i] = ((rev[i>>1]>>1) | (i&1)<<(b-1));
NTT(t,F,1),NTT(t,G,1);
F(i,0,t)F[i] = F[i] * G[i] % mod;
NTT(t,F,0);invn = ksm(t);
F(i,0,n+m)printf("%d ",int(F[i] * invn % mod));
printf("\n");
return cerr<<endl<<"Time:"<<clock()<<"ms"<<endl,0;
}
2. 多项式求逆
咕咕咕
参考资料:
快速傅里叶变换|快速数论变换
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探