洛谷.4512.[模板]多项式除法(NTT)
多项式除法 & 取模
很神奇,记录一下。
只是主要部分,更详细的和其它内容看这吧。
给定一个\(n\)次多项式\(A(x)\)和\(m\)次多项式\(D(x)\),求\(deg(Q)\leq n-m\)的多项式\(Q(x)\),满足$$A(x)=D(x)\times Q(x)+R(x)$$
其中\(R(x)\)可以看做是\(m-1\)次多项式(不足\(m-1\)次系数补\(0\))。
首先是想消除\(R(x)\)的影响。
对于一个\(n\)次多项式\(A(x)\),记$$AR(x)=xnA(\frac{1}{x})$$
例如\(A(x)=2x^3+4x^2-3x+1\),那么把\(\frac{1}{x}\)代进去,再乘个\(x^n\),可得$$AR(x)=x3-3x^2+4x+2$$
相当于将\(A(x)\)的系数进行了反转。
现在把\(A(x)=D(x)\times Q(x)+R(x)\)的\(x\)替换成\(\frac{1}{x}\),再乘\(x^n\),得
\]
要求\(Q(x)\),反转后次数仍不超过\(n-m\),而\(x^{n-m+1}R^R(x)\)最低项的次数\(>n-m\)。于是我们把式子放在模\(x^{n-m+1}\)意义下,\(R(x)\)就被消除了,且不影响\(Q(x)\)。而\(A(x),D(x)\)是已知的,不会有问题。
即$$A^R(x)\equiv D^R(x)\times QR(x) (mod x{n-m+1})$$
对\(D^R(x)\)求逆元,乘\(A^R(x)\)后可以得到\(Q^R(x)\),系数反转后即得\(Q(x)\)。\(A(x)-D(x)\times Q(x)\)就可以得到\(R(x)\)了。即多项式取模也可以这么做。
复杂度\(\mathcal O(n\log n)\)。
//640ms 7.55MB
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define P (998244353)
#define Rt (3)
#define i_Rt (332748118)
#define Mul(a,b) (1ll*a*b%P)
#define Sub(a,b) (a<b ? a-b+P : a-b)
#define Clear() memset(A,0,sizeof A),memset(B,0,sizeof B)
//#define gc() getchar()
#define MAXIN 200000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
const int N=(1<<18)+5;
int n,m,F[N],D[N],invD[N],Q[N],A[N],B[N],tmp[N],rev[N],W[N];//F = divisor * quotient + remainder
char IN[MAXIN],*SS=IN,*TT=IN;
inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now;
}
inline int FP(int x,int k)
{
int t=1;
for(; k; k>>=1, x=Mul(x,x))
if(k&1) t=Mul(x,t);
return t;
}
inline int Get_len(int x)
{
int len=1; while(len<=x) len<<=1;
return len;
}
void NTT(int *a,int len,int type)//polynomial
{
int lim=1, L=0;
while(lim<len) lim<<=1, ++L;
for(int i=1; i<lim; ++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<L-1);
for(int i=1; i<lim; ++i) if(i<rev[i]) std::swap(a[i],a[rev[i]]);
for(int i=2; i<=lim; i<<=1)
{
int mid=i>>1, Wn=FP(~type?Rt:i_Rt,(P-1)/i), t;//次数:(P-1)/i!
W[0]=1;
for(int j=1; j<mid; ++j) W[j]=Mul(W[j-1],Wn);
for(int j=0; j<lim; j+=i)
for(int k=0; k<mid; ++k)
a[j+k+mid]=(a[j+k]-(t=Mul(a[j+k+mid],W[k]))+P)%P,
a[j+k]=(a[j+k]+t)%P;
}
if(type==-1)
for(int i=0,inv=FP(lim,P-2); i<lim; ++i) a[i]=Mul(a[i],inv);
}
void P_Inv(int *f,int *g,int len)//len:要求逆元的长度
{
if(len==1) {g[0]=FP(f[0],P-2); return;}
P_Inv(f,g,len>>1);
for(int i=0; i<len; ++i) A[i]=f[i], B[i]=g[i];
NTT(A,len<<1,1), NTT(B,len<<1,1);
for(int i=0; i<len<<1; ++i) A[i]=Mul(A[i],Mul(B[i],B[i]));
NTT(A,len<<1,-1);
for(int i=0; i<len; ++i) g[i]=((g[i]<<1)%P-A[i]+P)%P;
}
void P_Mul(int *a,int *b,int *res,int l1,int l2)
{
int len=Get_len(l1+l2);
Clear();
for(int i=0; i<=l1; ++i) A[i]=a[i];
for(int i=0; i<=l2; ++i) B[i]=b[i];
NTT(A,len,1), NTT(B,len,1);
for(int i=0; i<len; ++i) res[i]=Mul(A[i],B[i]);
NTT(res,len,-1);
}
int main()
{
n=read(),m=read();
for(int i=0; i<=n; ++i) F[i]=read();
for(int i=0; i<=m; ++i) D[i]=read();
std::reverse(F,F+n+1), std::reverse(D,D+m+1);
P_Inv(D,invD,Get_len(n-m));
P_Mul(F,invD,Q,n-m,n-m);//次数都是n-m (mod x^{n-m+1})
std::reverse(Q,Q+n-m+1);
for(int i=0; i<=n-m; ++i) printf("%d ",Q[i]); putchar('\n');
std::reverse(F,F+n+1), std::reverse(D,D+m+1);
P_Mul(D,Q,tmp,m,n-m);
for(int i=0; i<m; ++i) printf("%d ",Sub(F[i],tmp[i]));
return 0;
}
洛谷.4512.[模板]多项式除法(NTT)的更多相关文章
- 洛谷.3803.[模板]多项式乘法(NTT)
题目链接:洛谷.LOJ. 为什么和那些差那么多啊.. 在这里记一下原根 Definition 阶 若\(a,p\)互质,且\(p>1\),我们称使\(a^n\equiv 1\ (mod\ p)\ ...
- 洛谷 P4512 [模板] 多项式除法
题目:https://www.luogu.org/problemnew/show/P4512 看博客:https://www.cnblogs.com/owenyu/p/6724611.html htt ...
- 洛谷.3803.[模板]多项式乘法(FFT)
题目链接:洛谷.LOJ. FFT相关:快速傅里叶变换(FFT)详解.FFT总结.从多项式乘法到快速傅里叶变换. 5.4 又看了一遍,这个也不错. 2019.3.7 叕看了一遍,推荐这个. #inclu ...
- 洛谷.4238.[模板]多项式求逆(NTT)
题目链接 设多项式\(f(x)\)在模\(x^n\)下的逆元为\(g(x)\) \[f(x)g(x)\equiv 1\ (mod\ x^n)\] \[f(x)g(x)-1\equiv 0\ (mod\ ...
- 洛谷.4721.[模板]分治FFT(NTT)
题目链接 换一下形式:\[f_i=\sum_{j=0}^{i-1}f_jg_{i-j}\] 然后就是分治FFT模板了\[f_{i,i\in[mid+1,r]}=\sum_{j=l}^{mid}f_jg ...
- 洛谷.4245.[模板]任意模数NTT(MTT/三模数NTT)
题目链接 三模数\(NTT\): 就是多模数\(NTT\)最后\(CRT\)一下...下面两篇讲的都挺明白的. https://blog.csdn.net/kscla/article/details/ ...
- 洛谷 P4238 [模板] 多项式求逆
题目:https://www.luogu.org/problemnew/show/P4238 看博客:https://www.cnblogs.com/xiefengze1/p/9107752.html ...
- 洛谷 P4245 [模板]任意模数NTT —— 三模数NTT / 拆系数FFT(MTT)
题目:https://www.luogu.org/problemnew/show/P4245 用三模数NTT做,需要注意时间和细节: 注意各种地方要取模!传入 upt() 里面的数一定要不超过2倍 m ...
- 洛谷P3373 [模板]线段树 2(区间增减.乘 区间求和)
To 洛谷.3373 [模板]线段树2 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.将某区间每一个数乘上x 3.求出某区间每一个数的和 输入输出格式 输入格 ...
随机推荐
- python入门 20141102-1405
那Python有哪些缺点呢? 第一个缺点就是运行速度慢,和C程序相比非常慢, 第二个缺点就是代码不能加密. Python是解释型的 不是编译型的 Python解释器-CPython 命令行: 只需要在 ...
- 《廖雪峰Git教程》学习笔记
原文链接 一.创建版本库 ①初始化一个Git仓库:git init ②添加文件到Git仓库:1.git add<file> ; 2.git commit 二.时光机穿梭 ①查看工作区状态 ...
- go 函数的作用域及可见性
1.全局变量,在程序整个生命周期有效 比如: test.go 中 我们定义 了 a 作为全局变量,那么在这个程序中任何地方都可以调用a, 这个 2. 局部变量,分为两种:1)函数内定义,2)语句块内定 ...
- python基础--类的经典类vs新式类
经典类VS新式类区别 1)写法新式类class Person(object):#new style 经典类class Persion: #classical style 2)调用父类 新式写法用sup ...
- tf.sequence_mask
tf.sequence_mask >>> x=[1,2,3]>>> z=tf.sequence_mask(x)>>> sess.run(z)arr ...
- Dropout caffe源码
GPU和CPU实现的不一样,这里贴的是CPU中的drop out 直接看caffe里面的源码吧:(产生满足伯努利分布的随机数mask,train的时候,data除以p,...... scale_ = ...
- html元素分类
目录 块级元素 内联元素 内联块级元素 总结 一.块级元素(block) 定义:默认情况下,独占一行的元素就是块级元素. 特点: 每个块级元素都从新的一行开始,并且其后的元素也另起一行.(真霸道,一 ...
- 激活Win10内置版Linux (ubuntu)
微软自从14316版本后,就开始原生支持Linux Bash命令行. 1.首先到系统设置——更新和安全——针对开发人员——选择开发者模式. 2.控制面板→程序和功能→启用或关闭Windows功能,勾 ...
- SQL Server日期计算
通常,你需要获得当前日期和计算一些其他的日期,例如,你的程序可能需要判断一个月的第一天或者最后一天.你们大部分人大概都知道怎样把日期进行分割(年.月.日等),然后仅仅用分割出来的年.月.日等放在几个函 ...
- Celery 框架学习笔记(生产者消费者模式)
生产者消费者模式 在实际的软件开发过程中,经常会碰到如下场景:某个模块负责产生数据,这些数据由另一个模块来负责处理(此处的模块是广义的,可以是类.函数.线程.进程等).产生数据的模块,就形象地称为生产 ...