多项式的求逆、取模和多点求值学习小记

  • 最近学习了多项式的求逆、取模和多点求值,这些方法能够解决很多多项式问题。

  • 这三个操作是环环相扣的,很有趣,学完后不妨记录一下。

多项式求逆

  • 给出一个次数界为 n n n 的多项式 A ( x ) A(x) A(x) ,需要求 B ( x ) B(x) B(x) 满足: A ( x ) B ( x ) ≡ 1 ( m o d   x n ) A(x)B(x)\equiv1(mod\ x^n) A(x)B(x)1(mod xn)

  • 我们考虑倍增来求解,假设我们已经知道 G ( x ) G(x) G(x) 满足: A ( x ) G ( x ) ≡ 1 ( m o d   x ⌊ n 2 ⌋ ) A(x)G(x)\equiv1(mod\ x^{\lfloor\frac{n}{2}\rfloor}) A(x)G(x)1(mod x2n)

  • 那么开始推导,两式相减: B ( x ) − G ( x ) ≡ 0 ( m o d   x ⌊ n 2 ⌋ ) B(x)-G(x)\equiv0(mod\ x^{\lfloor\frac{n}{2}\rfloor}) B(x)G(x)0(mod x2n)

  • 两边平方: B ( x ) 2 + G ( x ) 2 − 2 G ( x ) B ( x ) ≡ 0 ( m o d   x n ) B(x)^2+G(x)^2-2G(x)B(x)\equiv0(mod\ x^n) B(x)2+G(x)22G(x)B(x)0(mod xn)

  • 两边同乘 A ( x ) A(x) A(x) B ( x ) + A ( x ) G ( x ) 2 − 2 G ( x ) ≡ 0 ( m o d   x n ) B(x)+A(x)G(x)^2-2G(x)\equiv0(mod\ x^n) B(x)+A(x)G(x)22G(x)0(mod xn)

  • 于是可求得 B ( x ) B(x) B(x) B ( x ) ≡ 2 G ( x ) − A ( x ) G ( x ) 2 ( m o d   x n ) B(x)\equiv2G(x)-A(x)G(x)^2(mod\ x^n) B(x)2G(x)A(x)G(x)2(mod xn)

  • 当递归至 n = 1 n=1 n=1 时则有: B ( 0 ) = A ( 0 ) − 1 B(0)=A(0)^{-1} B(0)=A(0)1

  • 那么我们开始时就次数界 n n n 配成 2 k 2^k 2k 的形式,直接做倍增即可完成求逆。

  • 时间复杂度 O ( n   l o g   n ) O(n\ log\ n) O(n log n)

  • 模板题:洛谷 P4238 【模板】多项式求逆

多项式取模

  • 多项式取模是依赖于多项式求逆的。

  • 给出一个次数为 n n n 的多项式 A ( x ) A(x) A(x) ,一个次数为 m ( m ≤ n ) m(m\leq n) m(mn) 的多项式 B ( x ) B(x) B(x)

  • 我们需要求出多项式 C ( x ) C(x) C(x) R ( x ) R(x) R(x) 满足: A ( x ) = B ( x ) C ( x ) + R ( x )        ( ∗ ) A(x)=B(x)C(x)+R(x)\ \ \ \ \ \ (*) A(x)=B(x)C(x)+R(x)      ()

  • 其中 C ( x ) C(x) C(x) 的次数 ≤ n − m \leq n-m nm R ( x ) R(x) R(x) 的次数 &lt; m &lt;m <m

  • 考虑一个翻转操作: A R ( x ) = x n A ( 1 x ) A^R(x)=x^nA(\frac{1}{x}) AR(x)=xnA(x1)

  • 其实质就是多项式的系数翻转过来。

  • ( ∗ ) (*) () 式两边同乘 x n x^n xn 、并令 x = 1 x x=\frac{1}{x} x=x1 代入,可得: x n A ( 1 x ) = x m B ( 1 x ) ⋅ x n − m C ( 1 x ) + x n − m + 1 x m − 1 R ( 1 x ) x^nA(\frac{1}{x})=x^mB(\frac{1}{x})·x^{n-m}C(\frac{1}{x})+x^{n-m+1}x^{m-1}R(\frac{1}{x}) xnA(x1)=xmB(x1)xnmC(x1)+xnm+1xm1R(x1)

  • 则有: A R ( x ) = B R ( x ) C R ( x ) + x n − m + 1 R R ( x ) A^R(x)=B^R(x)C^R(x)+x^{n-m+1}R^R(x) AR(x)=BR(x)CR(x)+xnm+1RR(x)

  • 关键一步来了,把上式放在 ( m o d   x n − m + 1 ) (mod\ x^{n-m+1}) (mod xnm+1) 下,就能消除 R R ( x ) R^R(x) RR(x) 的影响(并且对 C R ( x ) C^R(x) CR(x) 无影响,其每一项次数都 ≤ n − m \leq n-m nm): A R ( x ) ≡ B R ( x ) C R ( x )    ( m o d   x n − m + 1 ) A^R(x)\equiv B^R(x)C^R(x)\ \ (mod\ x^{n-m+1}) AR(x)BR(x)CR(x)  (mod xnm+1)

  • 于是我们通过多项式求逆可以算出 C R ( x ) C^R(x) CR(x) C R ( x ) ≡ A R ( x ) B R ( x )    ( m o d   x n − m + 1 ) C^R(x)\equiv \frac{A^R(x)}{B^R(x)}\ \ (mod\ x^{n-m+1}) CR(x)BR(x)AR(x)  (mod xnm+1)

  • 之后翻转得到 C ( x ) C(x) C(x) ,回代 ( ∗ ) (*) () 式求出 R ( x ) R(x) R(x) 即可!

  • 以上各操作均是 O ( n   l o g   n ) O(n\ log \ n) O(n log n) 的。

  • 模板题:洛谷 P4512 【模板】多项式除法

多项式多点求值

  • 多点求值需要用到多项式取模!

  • 给出一个 n n n 次多项式 F ( x ) F(x) F(x) ,和 m m m 个值 x 1 , x 2 , ⋅ ⋅ ⋅ , x m x_1,x_2,···,x_m x1,x2,,xm ,要求 F ( x 1 ) , F ( x 2 ) , ⋅ ⋅ ⋅ , F ( x m ) F(x_1),F(x_2),···,F(x_m) F(x1),F(x2),,F(xm)

  • 比较关键的一个性质 F ( x )   m o d   ( x − a ) = F ( a ) F(x)\ mod\ (x-a)=F(a) F(x) mod (xa)=F(a)

  • 用因式定理或者直接将 ( x − a + a ) k (x-a+a)^k (xa+a)k 二项式展开都能很容易得到。

  • 那么我们依据这个来分治解决本问题。

  • L ( x ) = ∑ i = 1 ⌊ n 2 ⌋ ( x − x i ) L(x)=\sum_{i=1}^{\lfloor\frac{n}{2}\rfloor}(x-x_i) L(x)=i=12n(xxi) R ( x ) = ∑ i = ⌊ n 2 ⌋ + 1 n ( x − x i ) R(x)=\sum_{i=\lfloor\frac{n}{2}\rfloor+1}^{n}(x-x_i) R(x)=i=2n+1n(xxi)

  • 那么对于 1 ≤ i ≤ ⌊ n 2 ⌋ 1\leq i\leq\lfloor\frac{n}{2}\rfloor 1i2n F ( x i ) = ( F   m o d   L ) ( x i ) F(x_i)=(F\ mod\ L)(x_i) F(xi)=(F mod L)(xi) ;对于 ⌊ n 2 ⌋ + 1 ≤ i ≤ n \lfloor\frac{n}{2}\rfloor+1\leq i\leq n 2n+1in F ( x i ) = ( F   m o d   R ) ( x i ) F(x_i)=(F\ mod\ R)(x_i) F(xi)=(F mod R)(xi)。(原理同上述性质

  • 于是我们向线段树一样往下分治计算即可。

  • 具体来说就是先预处理出每个区间的 ∏ i = l r ( x − x i ) \prod_{i=l}^r(x-x_i) i=lr(xxi) (相当于是求出 L , R L,R L,R ,自下而上),

  • 之后再自上而下地把 F ( x ) F(x) F(x) 往下传,每次就模 L L L R R R 并分别传向左右子区间。

  • 当分治到底层( l = r l=r l=r)的时候, F ( x ) F(x) F(x) 就是被模剩一个常数项 F ′ ( 0 ) F&#x27;(0) F(0) 了,直接就有 F ( x i ) = F ′ ( 0 ) F(x_i)=F&#x27;(0) F(xi)=F(0)

  • 每次做多项式取模是 O ( n   l o g   n ) O(n\ log\ n) O(n log n) 的,套上分治就是 O ( n   l o g 2 n ) O(n\ log^2n) O(n log2n) 的了。当然常数很大……

  • 模板题:洛谷 P5050 【模板】多项式多点求值

  • 这里给出这题的代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
using namespace std;
typedef long long LL;
const int N=64005,M=16,G=3,mo=998244353;
int tot;
int f[N],p[N],ans[N];
int mul[N*M<<1],st[N<<2],en[N<<2];
int g[N*M<<1],stg[N<<2],eng[N<<2];
int a[N],b[N],c[N],rr[N];//a=b*c+rr
int ra[N],rb[N<<1],irb[N<<2];
int f1[N<<2],f2[N<<2],rev[N<<2],wn[N<<2];
inline int read()
{
    int X=0,w=0; char ch=0;
    while(!isdigit(ch)) w|=ch=='-',ch=getchar();
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
void write(int x)
{
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
inline int ksm(int x,int y)
{
    int s=1;
    while(y)
    {
        if(y&1) s=(LL)s*x%mo;
        x=(LL)x*x%mo;
        y>>=1;
    }
    return s;
}
inline void NTT(int *y,int len,int ff)
{
    for(int i=0;i<len;i++)
        if(i<rev[i]) swap(y[i],y[rev[i]]);
    for(int h=2,d=len>>1;h<=len;h<<=1,d>>=1)
    	for(int i=0,k=h>>1;i<len;i+=h)
    		for(int j=0,cnt=0;j<k;j++,cnt+=d)
    		{
    			int u=y[i+j],t=(LL)wn[cnt]*y[i+j+k]%mo;
    			y[i+j]=u+t>=mo?u+t-mo:u+t;
    			y[i+j+k]=u-t<0?u-t+mo:u-t;
			}
    if(ff==-1)
    {
    	for(int i=len>>1;i;i--) swap(y[i],y[len-i]);
    	int inv=ksm(len,mo-2);
    	for(int i=0;i<len;i++) y[i]=(LL)y[i]*inv%mo;
	}
}
void solve(int len,int num)
{
    if(len==1)
    {
        irb[0]=ksm(rb[0],mo-2);
        return;
    }
    solve(len>>1,num-1);
    for(int i=0;i<len;i++) rev[i]=rev[i>>1]>>1|(i&1)<<num-1;
    int w0=ksm(G,(mo-1)/len);
    for(int i=wn[0]=1;i<=len;i++) wn[i]=(LL)wn[i-1]*w0%mo;
    for(int i=0;i<len>>1;i++) f1[i]=rb[i];
    NTT(f1,len,1),NTT(irb,len,1);
    for(int i=0;i<len;i++) f1[i]=(2-(LL)f1[i]*irb[i]%mo+mo)*irb[i]%mo;
    NTT(f1,len,-1);
    for(int i=0;i<len>>1;i++) irb[i]=f1[i];
    for(int i=len>>1;i<len;i++) irb[i]=0;
}
void make(int v,int l,int r)
{
    if(l==r)
    {
        st[v]=++tot;
        mul[tot]=mo-p[l];
        en[v]=++tot;
        mul[tot]=1;
        return;
    }
    int mid=l+r>>1,ls=v<<1,rs=ls|1;
    make(ls,l,mid);
    make(rs,mid+1,r);
    int na=en[ls]-st[ls]+1,nb=en[rs]-st[rs]+1;
    int len=1,ll=0;
    while(len<na+nb) len<<=1,ll++;
    for(int i=0;i<len;i++) rev[i]=rev[i>>1]>>1|(i&1)<<ll-1;
    int w0=ksm(G,(mo-1)/len);
    for(int i=wn[0]=1;i<=len;i++) wn[i]=(LL)wn[i-1]*w0%mo;
    for(int i=0;i<na;i++) f1[i]=mul[st[ls]+i];
    for(int i=na;i<len;i++) f1[i]=0;
    for(int i=0;i<nb;i++) f2[i]=mul[st[rs]+i];
    for(int i=nb;i<len;i++) f2[i]=0;
    NTT(f1,len,1),NTT(f2,len,1);
    for(int i=0;i<len;i++) f1[i]=(LL)f1[i]*f2[i]%mo;
    NTT(f1,len,-1);
    na+=nb;
    while(na>1 && !f1[na-1]) na--;
    st[v]=tot+1;
    for(int i=0;i<na;i++) mul[++tot]=f1[i];
    en[v]=tot;
}
void find(int v,int l,int r,int fa)
{
    int na=eng[fa]-stg[fa],nb=en[v]-st[v];
    if(na>=nb)
    {
        for(int i=0;i<=na;i++) a[i]=g[stg[fa]+i];
        for(int i=0;i<=nb;i++) b[i]=mul[st[v]+i];
        for(int i=0;i<=na-nb;i++) ra[i]=a[na-i];
        for(int i=0;i<=nb;i++) rb[i]=b[nb-i];
        for(int i=na-nb+1;i<=nb;i++) rb[i]=0;
        int len=1,ll=0;
        while(len<(na-nb+1)*2) len<<=1,ll++;
        for(int i=0;i<len;i++) f1[i]=0;
        solve(len,ll);
        for(int i=0;i<=na-nb;i++) f1[i]=ra[i],f2[i]=irb[i];
        for(int i=na-nb+1;i<len;i++) f1[i]=f2[i]=0;
        NTT(f1,len,1),NTT(f2,len,1);
        for(int i=0;i<len;i++) f1[i]=(LL)f1[i]*f2[i]%mo;
        NTT(f1,len,-1);
        for(int i=0;i<=na-nb;i++) c[na-nb-i]=f1[i];
        len=1,ll=0;
        while(len<nb<<1) len<<=1,ll++;
        for(int i=0;i<len;i++) rev[i]=rev[i>>1]>>1|(i&1)<<ll-1;
    	int w0=ksm(G,(mo-1)/len);
    	for(int i=wn[0]=1;i<=len;i++) wn[i]=(LL)wn[i-1]*w0%mo;
        for(int i=0;i<nb;i++) f1[i]=b[i],f2[i]=c[i];
        for(int i=nb;i<len;i++) f1[i]=f2[i]=0;
        NTT(f1,len,1),NTT(f2,len,1);
        for(int i=0;i<len;i++) f1[i]=(LL)f1[i]*f2[i]%mo;
        NTT(f1,len,-1);
        for(int i=0;i<nb;i++) rr[i]=(a[i]-f1[i]+mo)%mo;
        for(int i=0;i<=max(na,nb);i++) a[i]=b[i]=c[i]=ra[i]=rb[i]=irb[i]=0;
        while(nb>1 && !rr[nb-1]) nb--;
        stg[v]=tot+1;
        for(int i=0;i<nb;i++) g[++tot]=rr[i],rr[i]=0;
        eng[v]=tot;
    }else
    {
        stg[v]=tot+1;
        for(int i=stg[fa];i<=eng[fa];i++) g[++tot]=g[i];
        eng[v]=tot;
    }
    if(l==r)
    {
        ans[l]=g[stg[v]];
        return;
    }
    int mid=l+r>>1;
    find(v<<1,l,mid,v);
    find(v<<1|1,mid+1,r,v);
}
int main()
{
    int n=read(),m=read();
    for(int i=0;i<=n;i++) f[i]=read();
    for(int i=1;i<=m;i++) p[i]=read();
    make(1,1,m);
    stg[tot=0]=1;
    for(int i=0;i<=n;i++) g[++tot]=f[i];
    eng[0]=tot;
    find(1,1,m,0);
    for(int i=1;i<=m;i++) write(ans[i]),putchar('\n');
    return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值