多项式除法 学习笔记 洛谷4512

题目链接

题意:
给定一个 n n n次多项式 F ( x ) F(x) F(x)和一个 m m m次多项式 G ( x ) G(x) G(x),请求出多项式 Q ( x ) Q(x) Q(x), R ( x ) R(x) R(x),满足以下条件:
Q ( x ) Q(x) Q(x)次数为 n − m n-m nm R ( x ) R(x) R(x)次数小于 m m m F ( x ) = Q ( x ) ∗ G ( x ) + R ( x ) F(x) = Q(x) * G(x) + R(x) F(x)=Q(x)G(x)+R(x)。所有的运算在模 998244353 998244353 998244353意义下进行。 n &lt; = 100000 n&lt;=100000 n<=100000

题解:
我们来介绍一下多项式除法。前置知识:多项式求逆、NTT。

我们定义一个 A R ( x ) = x n A ( 1 x ) A^R(x)=x^nA(\frac{1}{x}) AR(x)=xnA(x1),我们把右边展开,可以发现其实与 A ( x ) A(x) A(x)的区别就是原来 x , x 2 , x 3 . . . x n x,x^2,x^3...x^n x,x2,x3...xn的系数分别是 a 1 , a 2 , a 3 . . . a n a_1,a_2,a_3...a_n a1,a2,a3...an,现在变成了 a n , a n − 1 . . . a 1 a_n,a_{n-1}...a_1 an,an1...a1,也就是翻转了系数。
我们本来有 F ( x ) = Q ( x ) ∗ G ( x ) + R ( x ) F(x) = Q(x) * G(x) + R(x) F(x)=Q(x)G(x)+R(x) 现在等式两边同时乘 x n x^n xn,并且把每个函数尽可能构造成用 A R A^R AR型的函数来表示,我们可以得到: x n ∗ F ( x ) = x n − m ∗ Q ( x ) ∗ x m ∗ G ( x ) + x n − m + 1 ∗ x m − 1 R ( x ) x^n*F(x) = x^{n-m}*Q(x) * x^m*G(x) + x^{n-m+1}*x^{m-1}R(x) xnF(x)=xnmQ(x)xmG(x)+xnm+1xm1R(x) F R ( x ) = Q R ( x ) ∗ G R ( x ) + x n − m + 1 R R F^R(x)=Q^R(x)*G^R(x)+x^{n-m+1}R^R FR(x)=QR(x)GR(x)+xnm+1RR我们等式两边同时模一个 x n − m + 1 x^{n-m+1} xnm+1,就可以把 x n − m + 1 ∗ x m − 1 R ( x ) x^{n-m+1}*x^{m-1}R(x) xnm+1xm1R(x)消掉,同时不会影响到 x n − m ∗ Q ( x ) x^{n-m}*Q(x) xnmQ(x),因为这个多项式的最高次数只有 n − m n-m nm。这样就变成了 F R ( x ) ≡ Q R ( x ) ∗ G R ( x ) ( m o d   x n − m + 1 ) F^R(x)\equiv Q^R(x)*G^R(x)(mod\ x^{n-m+1}) FR(x)QR(x)GR(x)(mod xnm+1) 我们已知了 F ( x ) F(x) F(x) G ( x ) G(x) G(x)的各项系数,那么只要系数翻转一下就可以得到对应的 F R ( x ) F^R(x) FR(x) G R ( x ) G^R(x) GR(x)的各项系数,我们要求 Q ( x ) Q(x) Q(x)的系数,可以先求出 Q R ( x ) Q^R(x) QR(x)的系数,再翻转回来就好了。所以我们把式子变成 Q R ( x ) ≡ F R ( x ) ∗ G − R ( x ) ( m o d   x n − m + 1 ) Q^R(x)\equiv F^R(x)*G^{-R}(x)(mod\ x^{n-m+1}) QR(x)FR(x)GR(x)(mod xnm+1) 我们发现对于 G − R G^{-R} GR,我们需要对 G R ( x ) G^R(x) GR(x)进行一次多项式求逆。然后求出 Q ( x ) Q(x) Q(x)之后我们再用 R ( x ) = F ( x ) − Q ( x ) ∗ G ( x ) R(x)=F(x)-Q(x)*G(x) R(x)=F(x)Q(x)G(x)算出 R ( x ) R(x) R(x)的各项系数即可。

有了多项式除法之后我们还可以做一些多项式取模、多项式gcd之类的东西。

代码:

#include <bits/stdc++.h>
using namespace std;

int n,m,l,rev[500010];
long long a[500010],b[500010],aa[500010],bb[500010],c[500010];
const long long mod=998244353,g=3,gi=332748118;
inline int read()
{
    int x=0;
    char s=getchar();
    while(s>'9'||s<'0')
    s=getchar();
    while(s>='0'&&s<='9')
    {
        x=x*10+s-'0';
        s=getchar();
    }
    return x;
}
inline long long ksm(long long x,long long y)
{
    long long res=1;
    while(y)
    {
        if(y&1)
        res=res*x%mod;
        x=x*x%mod;
        y>>=1;
    }
    return res;
}
inline void ntt(long long *a,int dft,int len)
{
    l=0;
    int ji=len;
    for(len=1;len<ji;len<<=1)
    ++l;
    for(int i=0;i<len;++i)
    rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1));
    for(int i=0;i<len;++i)
    {
        if(i<rev[i])
        swap(a[i],a[rev[i]]);
    }
    for(int i=1;i<len;i<<=1)
    {
        long long wn=ksm((dft==1?g:gi),(mod-1)/(i<<1));
        for(int j=0,p=(i<<1);j<len;j+=p)
        {
            long long w=1;
            for(int k=0;k<i;++k)
            {
                long long x=a[j+k],y=w*a[i+j+k]%mod;
                a[j+k]=(x+y)%mod;
                a[i+j+k]=(x-y+mod)%mod;
                w=w*wn%mod;
            }
        } 
    }
    if(dft==-1)
    {
        int ni=ksm(ji,mod-2);
        for(int i=0;i<len;++i)
        a[i]=a[i]*ni%mod;
    }
}
inline void inv(long long *a,long long *b,int len)
{
    if(len==1)
    {
        b[0]=ksm(a[0],mod-2);
        return;
    }
    inv(a,b,len>>1);
    for(int i=0;i<len;++i)
    {
        aa[i]=a[i];
        bb[i]=b[i];
    }
    ntt(aa,1,len<<1);
    ntt(bb,1,len<<1);
    for(int i=0;i<(len<<1);++i)
    aa[i]=aa[i]*bb[i]%mod*bb[i]%mod;
    ntt(aa,-1,len<<1);
    for(int i=0;i<len;++i)
    b[i]=(b[i]*2%mod-aa[i]+mod)%mod;
    for(int i=0;i<(len<<1);++i)
    {
        aa[i]=0;
        bb[i]=0;
    }
}
int main()
{
    n=read();
    m=read();
    for(int i=0;i<=n;++i)
    a[i]=read();
    for(int i=0;i<=m;++i)
    b[i]=read();
    reverse(a,a+n+1);
    reverse(b,b+m+1);
    for(l=1;l<=n-m;l<<=1);
    inv(b,c,l);	
    int ji;
    for(ji=1;ji<=(n-m);ji<<=1);
    ji<<=1;
    for(int i=0;i<=n-m;++i)
    aa[i]=a[i];
    ntt(aa,1,ji);
    ntt(c,1,ji);
    for(int i=0;i<=ji;++i)
    c[i]=c[i]*aa[i]%mod;
    ntt(c,-1,ji);
    reverse(c,c+n-m+1);
    for(int i=n-m+1;i<=2*n;++i)
    c[i]=0;
    for(int i=0;i<=n-m;++i)
    printf("%lld ",c[i]);
    printf("\n");
    reverse(a,a+n+1);
    reverse(b,b+m+1);
    for(ji=1;ji<=max(n-m,m);ji<<=1);
    ji<<=1;	
    ntt(c,1,ji);
    ntt(b,1,ji);
    for(int i=0;i<=ji;++i)
    b[i]=1ll*c[i]*b[i]%mod;
    ntt(b,-1,ji);
    for(int i=0;i<m;++i)
    printf("%lld ",(a[i]-b[i]+mod)%mod);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值