『基础多项式算法总结』

在教练的要求下开始学习多项式算法了,不过因为不太会积分和求导先把多项式牛顿迭代,多项式指数函数,多项式幂函数,多项式快速幂等内容咕掉了,于是这一篇博客就是其他基础多项式内容的总结。


Fast Fourier Transform

\(FFT\),快速傅里叶变换,可以在\(O(n\log_2n)\)的时间内计算多项式乘法。

先回忆一下\(FFT\)的思路,首先是多项式的系数表达法,如果直接计算的话,时间复杂度是\(O(n^2)\)的,可以用分治算法优化到\(O(n^{log_23})\),通常没有其他更优的算法了。

然后就是考虑多项式的另一个表达方式,点值表达法,这样就可以实现\(O(n)\)乘法。

但是使用普通的求值方法和拉格朗日插值法实现系数表达法和点值表达法的转换是\(O(n^2)\)的,毫无意义。

后来一个分治算法出现了,我们可以在求值的时候代入\(n\)个单位根,根据单位根的性质,我们可以只计算一半的值得到另一半,从而递归下去分治解决,实现时间复杂度\(O(n\log_2n)\)。

说到单位根,就必须提一下有关复数的基础知识,可以看这篇博客

然后就是单位根的性质了:

\(1.\) \(\omega_{n}^k=\cos k\frac{2\pi}{n}+i\sin k\frac{2\pi}{n}\)

\(2.\) \(\omega_{2n}^{2k}=\omega_n^k\)

\(3.\) \(\omega_{n}^{k+\frac{n}{2}}=-\omega_{n}^{k}\)

\(4.\) \(k\not=0\)时,\(\sum_{i=0}^{n-1}(\omega_{n}^{k})^i=0\),\(k=0\)时,\(\sum_{i=0}^{n-1}(\omega_{n}^{k})^i=n\)。

然后把多项式下标按奇偶性分类,就能分治了。

通过构造可知,我们可以用同样的方式也可以把点值表达法转换回系数表达法,需要除以一个序列长度的大小。

最后的最后就是把递归的\(FFT\)用迭代实现,就是一点二进制的小\(Trick\),叫蝴蝶定理,找规律的事。

可以看这篇博客

\(Code:\)

#include <bits/stdc++.h>
using namespace std;
const int N = (1<<22) + 20;
namespace Mathcalc
{
    const double PI = acos(-1.0) , eps = 5e-3;
    struct Complex
    {
        double x,y;
        Complex ( double _x = 0 , double _y = 0 ) { x = _x , y = _y; }
        friend Complex operator + (Complex a,Complex b) { return Complex( a.x + b.x , a.y + b.y ); }
        friend Complex operator - (Complex a,Complex b) { return Complex( a.x - b.x , a.y - b.y ); }
        friend Complex operator * (Complex a,Complex b) { return Complex( a.x*b.x - a.y*b.y , a.x*b.y + a.y*b.x ); }
    };
}
using namespace Mathcalc;
inline int read(void)
{
    int x = 0 , w = 0; char ch = ' ';
    while ( !isdigit(ch) ) w |= ch=='-' , ch = getchar();
    while ( isdigit(ch) ) x = x*10 + ch-48 , ch = getchar();
    return w ? -x : x;
}
int n,m,lenth,cnt,rev[N];
Complex a[N],b[N],c[N];
inline void input(void)
{
    n = read() , m = read();
    for (int i=0;i<=n;i++) a[i].x = read();
    for (int i=0;i<=m;i++) b[i].x = read();
}
inline void calcrev(void)
{
    lenth = 1;
    while ( lenth <= n + m ) lenth <<= 1 , cnt++;
    for (int i=0;i<lenth;i++)
        rev[i] = ( rev[i>>1] >> 1 ) | ( (i&1) << (cnt-1) );
}
inline void FFT(Complex *p,int op)
{
    for (int i=0;i<lenth;i++)
        if ( i < rev[i] ) swap( p[i] , p[rev[i]] );
    for (int i=1;i<lenth;i<<=1)
    {
        Complex w ( cos(PI/i) , op * sin(PI/i) );
        for (int j=0;j<lenth;j+=i<<1)
        {
            Complex omega ( 1 , 0 );
            for (int k=0;k<i;k++)
            {
                Complex x = p[j+k] , y = omega * p[i+j+k];
                p[j+k] = x + y , p[i+j+k] = x - y;
                omega = omega * w;
            }
        }
    }
    if ( op == -1 )
        for (int i=0;i<lenth;i++)
            p[i].x /= lenth;
}
int main(void)
{
    input();
    calcrev();
    FFT( a , 1 ) , FFT( b , 1 );
    for (int i=0;i<lenth;i++)
        c[i] = a[i] * b[i];
    FFT( c , -1 );
    for (int i=0;i<=n+m;i++)
        printf("%d ",int(c[i].x + eps));
    return 0;
}

Number Theoretic Transform

\(NTT\),快速数论变换,可以在\(O(n\log_2n)\)的时间内计算模意义下的多项式乘法。

思路和\(FFT\)是一模一样的,关键在于我们在模意义下找到了单位根的替代品:原根,满足单位根的所有性质。

不需要用到太多数论知识,可以看这篇博客和这篇博客

然后按照\(FFT\)的方式来就可以了。

但是\(NTT\)的模数是有限定的,可以看这个

\(Code:\)

#include <bits/stdc++.h>
using namespace std;
const int N = (1<<22)+20;
namespace Mathcalc
{
    const int Mod = 998244353 , G = 3 , INVG = 332748118;
    inline int add(int a,int b) { return a + b >= Mod ? a + b - Mod : a + b; }
    inline int sub(int a,int b) { return a - b < 0 ? a - b + Mod : a - b; }
    inline int mul(int a,int b) { return 1LL * a * b % Mod; }
    inline void Mul(int &a,int b) { a = mul( a , b ); }
    inline int quickpow(int a,int b) { int res = 1; for (;b;Mul(a,a),b>>=1) if ( b & 1 ) Mul( res , a ); return res; }
    inline int inv(int a) { return quickpow( a , Mod-2 ); }
};
using namespace Mathcalc;
inline int read(void)
{
    int x = 0 , w = 0; char ch = ' ';
    while ( !isdigit(ch) ) w |= ch=='-' , ch = getchar();
    while ( isdigit(ch) ) x = x*10 + ch-48 , ch = getchar();
    return w ? -x : x;
}
int n,m,lenth,cnt,rev[N];
int a[N],b[N],c[N];
inline void input(void)
{
    n = read() - 1 , m = read() - 1;
    for (int i=0;i<=n;i++) a[i] = read();
    for (int i=0;i<=m;i++) b[i] = read();
}
inline void calcrev(void)
{
    lenth = 1;
    while ( lenth <= n + m ) lenth <<= 1 , cnt++;
    for (int i=0;i<lenth;i++)
        rev[i] = ( rev[i>>1] >> 1 ) | ( (i&1) << (cnt-1) );
}
inline void NTT(int *p,int op)
{
    for (int i=0;i<lenth;i++)
        if ( i < rev[i] ) swap( p[i] , p[rev[i]] );
    for (int i=1;i<lenth;i<<=1)
    {
        int w = quickpow( op == 1 ? G : INVG , (Mod-1) / (i<<1) );
        for (int j=0;j<lenth;j+=i<<1)
        {
            int omega = 1;
            for (int k=0;k<i;k++)
            {
                int x = p[j+k] , y = mul( omega , p[i+j+k] );
                p[j+k] = add( x , y ) , p[i+j+k] = sub( x , y );
                Mul( omega , w );
            }
        }
    }
    if ( op == -1 )
    {
        int val = inv( lenth );
        for (int i=0;i<lenth;i++)
            Mul( p[i] , val );
    }
}
int main(void)
{
    input();
    calcrev();
    NTT( a , 1 ) , NTT( b , 1 );
    for (int i=0;i<lenth;i++)
        c[i] = mul( a[i] , b[i] );
    NTT( c , -1 );
    for (int i=0;i<=n+m;i++)
        printf("%d ",c[i]);
    return 0;
}

分治FFT

我们知道,\(FFT/NTT\)是可以求形容\(c_k=\sum_{i+j=k}a_i\times b_j\)的卷积的,但是如果是长这样的卷积呢?

\[f_k=\sum_{i+j=k}f_i\times g_j\]

仿佛套一个\(cdq\)分治算贡献就可以了。

没错,事实上也就是这样,中间的卷积还是要\(NTT\),时间复杂度\(O(n\log^2n)\)。

\(Cocd:\)

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+20 , SIZE = (1<<18)+20;
const int Mod = 998244353 , G = 3 , INVG = 332748118;
int n,cnt,lenth,rev[SIZE];
long long f[N],g[N],a[SIZE],b[SIZE],c[SIZE];
inline int read(void)
{
    int x = 0 , w = 0; char ch = ' ';
    while ( !isdigit(ch) ) w |= ch=='-' , ch = getchar();
    while ( isdigit(ch) ) x = x*10 + ch-48 , ch = getchar();
    return w ? -x : x;
}
inline void input(void)
{
    n = read() - 1 , f[0] = 1;
    for (int i=1;i<=n;i++)
        g[i] = read();
}
inline long long quickpow(long long a,long long b)
{
    long long res = 1;
    while ( b )
    {
        if ( 1 & b ) res = res * a % Mod;
        b >>= 1 , a = a * a % Mod;
    }
    return res;
}
inline void NTT(long long *p,int op)
{
    for (int i=0;i<lenth;i++)
        if ( i < rev[i] ) swap( p[i] , p[rev[i]] );
    for (int i=1;i<lenth;i<<=1)
    {
        long long w = quickpow( op == 1 ? G : INVG , (Mod-1) / (i<<1) );
        for (int j=0;j<lenth;j+=i<<1)
        {
            long long omega = 1;
            for (int k=0;k<i;k++)
            {
                long long x = p[j+k] , y = omega * p[i+j+k] % Mod;
                p[j+k] = ( x + y ) % Mod;
                p[i+j+k] = ( ( x - y ) % Mod + Mod ) % Mod;
                omega = omega * w % Mod;
            }
        }
    }
}
inline void cdq(int l,int r)
{
    if ( l == r ) return void();
    int mid = l+r >> 1 , len = (r-l-1) + (mid-l);
    cdq( l , mid );
    lenth = 1 , cnt = 0;
    while ( lenth <= len ) lenth <<= 1 , cnt++;
    for (int i=0;i<lenth;i++)
        rev[i] = ( rev[i>>1] >> 1 ) | ( (i&1) << (cnt-1) ),
        a[i] = b[i] = c[i] = 0;
    for (int i=l;i<=mid;i++) a[i-l] = f[i];
    for (int i=1;i<=r-l;i++) b[i-1] = g[i];
    NTT( a , 1 ) , NTT( b , 1 );
    for (int i=0;i<lenth;i++) c[i] = a[i] * b[i] % Mod;
    NTT( c , -1 );
    long long inv = quickpow( lenth , Mod-2 );
    for (int i=0;i<=len;i++)
        c[i] = c[i] * inv % Mod;
    for (int i=mid+1;i<=r;i++)
        f[i] = ( f[i] + c[i-l-1] ) % Mod;
    cdq( mid + 1 , r );
}
int main(void)
{
    input();
    cdq( 0 , n );
    for (int i=0;i<=n;i++)
        printf("%lld ",f[i]);
    puts("");
    return 0;
}

Fast Walsh-Hadamard Transform

\(FWT\),快速沃尔什变换,用于计算位运算卷积。

形式是这样的\(c_k=\sum_{i\ op\ j=k}a_i\times b_j\),\(op\)是\(and,or,xor\)中的一个位运算。

思路和\(FWT\)是一样的,考虑先对系数表达法做一点变换,然后点乘\(O(n)\)解决问题,再逆变换回来就可以了。

好像不要那么多公式?看这三篇博客吧:123

\(Code:\)

#include <bits/stdc++.h>
using namespace std;
const int N = 20;
namespace Mathcalc
{
    const int Mod = 998244353 , INV2 = 499122177;
    inline int add(int a,int b) { return a + b >= Mod ? a + b - Mod : a + b; }
    inline int sub(int a,int b) { return a - b < 0 ? a - b + Mod : a - b; }
    inline int mul(int a,int b) { return 1LL * a * b % Mod; }
    inline void Add(int &a,int b) { a = add( a , b ); }
    inline void Sub(int &a,int b) { a = sub( a , b ); }
    inline void Mul(int &a,int b) { a = mul( a , b ); }
};
using namespace Mathcalc;
int n; int a[1<<N],b[1<<N],c[1<<N];
inline void input(void)
{
    scanf("%d",&n);
    for (int i=0;i<1<<n;i++)
        scanf("%d",&a[i]);
    for (int i=0;i<1<<n;i++)
        scanf("%d",&b[i]);
}
inline void FWTor(int *p,int op)
{
    for (int i=1;i<1<<n;i<<=1)
        for (int j=0;j<1<<n;j+=i<<1)
            for (int k=0;k<i;k++)
                if ( op == 1 ) Add( p[i+j+k] , p[j+k] );
                else Sub( p[i+j+k] , p[j+k] );
}
inline void FWTand(int *p,int op)
{
    for (int i=1;i<1<<n;i<<=1)
        for (int j=0;j<1<<n;j+=i<<1)
            for (int k=0;k<i;k++)
                if ( op == 1 ) Add( p[j+k] , p[i+j+k] );
                else Sub( p[j+k] , p[i+j+k] );
}
inline void FWTxor(int *p,int op)
{
    for (int i=1;i<1<<n;i<<=1)
        for (int j=0;j<1<<n;j+=i<<1)
            for (int k=0;k<i;k++)
            {
                int x = p[j+k] , y = p[i+j+k];
                p[j+k] = mul( op , add( x , y ) );
                p[i+j+k] = mul( op , sub( x , y ) );
            }
}
inline void solve(void)
{
    FWTor( a , 1 ) , FWTor( b , 1 );
    for (int i=0;i<1<<n;i++) c[i] = mul( a[i] , b[i] );
    FWTor( c , -1 );
    for (int i=0;i<1<<n;i++) printf("%d ",c[i]);
    puts("") , FWTor( a , -1 ) , FWTor( b , -1 );

    FWTand( a , 1 ) , FWTand( b , 1 );
    for (int i=0;i<1<<n;i++) c[i] = mul( a[i] , b[i] );
    FWTand( c , -1 );
    for (int i=0;i<1<<n;i++) printf("%d ",c[i]);
    puts("") , FWTand( a , -1 ) , FWTand( b , -1 );

    FWTxor( a , 1 ) , FWTxor( b , 1 );
    for (int i=0;i<1<<n;i++) c[i] = mul( a[i] , b[i] );
    FWTxor( c , INV2 );
    for (int i=0;i<1<<n;i++) printf("%d ",c[i]);
    puts("") , FWTxor( a , INV2 ) , FWTxor( b , INV2 );
}
int main(void)
{
    input();
    solve();
    return 0;
}

多项式求逆

给定多项式\(F(x)\),求多项式\(G(x)\),满足\(F(x)G(x)\equiv1\pmod {x^n}\)。

当多项式只有一个常数项的时候,求逆元即可。

反之,考虑一个倍增算法:

假设\(F\times G'\equiv 1\pmod{x^{\frac{n}{2}}}\),我们要求\(F\times G\equiv1\pmod{x^n}\)。

\[F\times(G-G')\equiv 0\pmod {x^{\frac{n}{2}}}\\ \ \\G-G'\equiv 0\pmod {x^{\frac{n}{2}}}\\\ \\(G-G')^2\equiv 0\pmod {x^n}\\\ \\G^2-2GG'+G'^2\equiv 0\pmod{x^n}\\ \ \\F\times(G^2-2GG'+G'^2)\equiv 0\pmod{x^n}\\\ \\G-2G'+FG'^2\equiv 0\pmod{x^n}\\ \ \\G\equiv2G'+FG'^2\pmod{x^n}\]

结合\(NTT\),就可以倍增计算了,时间复杂度\(O(n\log_2n)\)。

\(Code:\)

#include <bits/stdc++.h>
using namespace std;
const int N = (1<<20)+20;
namespace Mathcalc
{
    const int Mod = 998244353 , G = 3 , INVG = 332748118;
    inline int add(int a,int b) { return a + b >= Mod ? a + b - Mod : a + b; }
    inline int sub(int a,int b) { return a - b < 0 ? a - b + Mod : a - b; }
    inline int mul(int a,int b) { return 1LL * a * b % Mod; }
    inline void Add(int &a,int b) { a = add( a , b ); }
    inline void Sub(int &a,int b) { a = sub( a , b ); }
    inline void Mul(int &a,int b) { a = mul( a , b ); }
    inline int quickpow(int a,int b) { int res = 1; for (;b;Mul(a,a),b>>=1) if ( b & 1 ) Mul( res , a ); return res; }
    inline int inv(int a) { return quickpow( a , Mod-2 ); }
};
using namespace Mathcalc;
inline int read(void)
{
    int x = 0 , w = 0; char ch = ' ';
    while ( !isdigit(ch) ) w |= ch=='-' , ch = getchar();
    while ( isdigit(ch) ) x = x*10 + ch-48 , ch = getchar();
    return w ? -x : x;
}
int temp[N],rev[N];
inline void calcrev(int len,int cnt)
{
    for (int i=0;i<len;i++)
        rev[i] = ( rev[i>>1] >> 1 ) | ( (i&1) << (cnt-1) );
}
inline void NTT(int *p,int op,int len)
{
    for (int i=0;i<len;i++)
        if ( i < rev[i] ) swap( p[i] , p[rev[i]] );
    for (int i=1;i<len;i<<=1)
    {
        int w = quickpow( op == 1 ? G : INVG , (Mod-1) / (i<<1) );
        for (int j=0;j<len;j+=i<<1)
        {
            int omega = 1;
            for (int k=0;k<i;k++)
            {
                int x = p[j+k] , y = mul( omega , p[i+j+k] );
                p[j+k] = add( x , y ) , p[i+j+k] = sub( x , y );
                Mul( omega , w );
            }
        }
    }
    if ( op == -1 )
    {
        int val = inv( len );
        for (int i=0;i<len;i++)
            Mul( p[i] , val );
    }
}
inline void Polyinv(int *a,int *b,int n)
{
    int p = 1; while ( p < n ) p <<= 1;
    for (int i=0;i<p;i++) b[i] = 0;
    b[0] = inv( a[0] );
    for (int len=1,cnt=0;len<=p;len<<=1,cnt++)
    {
        calcrev( len<<1 , cnt+1 );
        for (int i=0;i<len;i++) temp[i] = a[i];
        for (int i=len;i<len<<1;i++) temp[i] = 0;
        NTT( b , 1 , len<<1 ) , NTT( temp , 1 , len<<1 );
        for (int i=0;i<len<<1;i++)
            Mul( b[i] , sub( 2 , mul( temp[i] , b[i] ) ) );
        NTT( b , -1 , len<<1 );
        for (int i=len;i<len<<1;i++) b[i] = 0;
    }
    for (int i=n;i<p;i++) b[i] = 0;
}
int n,m,a[N],b[N];
inline void input(void)
{
    n = read() , m = read();
    for (int i=0;i<n;i++) a[i] = read();
}
int main(void)
{
    input();
    Polyinv( a , b , m );
    for (int i=0;i<m;i++)
        printf("%d ",b[i]);
    return 0;
}

多项式除法

给定\(n\)次多项式\(F(x)\)和\(m\)次多项式\(G(x)\),求多项式\(Q(x),R(x)\),满足:

\(1.\) \(Q(x)\)为\(n-m\)次多项式,\(R(x)\)的次数小于\(m\)。
\(2.\) \(F(x)=G(x)\times Q(x)+R(x)\)。

考虑一个操作\(rev\),对于多项式\(F(x)\),\(F^{rev}(x)=x^nF(\frac{1}{x})\),就是系数翻转。

然后考虑推一波式子:
\[
F(x)=Q(x)\times G(x)+R(x)
\\\ \F\left (\frac{1}{x} \right )=G\left (\frac{1}{x} \right ) \times Q\left (\frac{1}{x} \right )+R\left (\frac{1}{x} \right )
\\ \ \x^nF\left (\frac{1}{x} \right )=x^mG\left (\frac{1}{x} \right ) \times x^{n-m} Q\left (\frac{1}{x} \right )+x^{n-m+1}x^{m-1}R\left (\frac{1}{x} \right )
\\ \ \F^{rev}(x)=Q^{rev}(x)\times G^{rev}(x)+x^{n-m+1}R^{rev}(x)
\\ \ \F^{rev}(x)=Q^{rev}(x)\times G^{rev}(x)\pmod{x^{n-m+1}}
\\ \ \Q^{rev}(x)=F^{rev}(x)\times (G^{rev})^{-1}(x)\pmod{x^{n-m+1}}
\]

考虑对\(G^{rev}\)做一个多项式求逆,然后再用\(NTT\)把它和\(F^{rev}\)乘起来就是\(Q^{rev}\)了。

然后\(R\)直接算就可以了:

\[R(x)=F(x)-Q(x)\times G(x)\]

\(Code:\)

#include <bits/stdc++.h>
using namespace std;
const int N = (1<<20)+20;
namespace Mathcalc
{
    const int Mod = 998244353 , G = 3 , INVG = 332748118;
    inline int add(int a,int b) { return a + b >= Mod ? a + b - Mod : a + b; }
    inline int sub(int a,int b) { return a - b < 0 ? a - b + Mod : a - b; }
    inline int mul(int a,int b) { return 1LL * a * b % Mod; }
    inline void Add(int &a,int b) { a = add( a , b ); }
    inline void Sub(int &a,int b) { a = sub( a , b ); }
    inline void Mul(int &a,int b) { a = mul( a , b ); }
    inline int quickpow(int a,int b) { int res = 1; for (;b;Mul(a,a),b>>=1) if ( b & 1 ) Mul( res , a ); return res; }
    inline int inv(int a) { return quickpow( a , Mod-2 ); }
};
using namespace Mathcalc;
inline int read(void)
{
    int x = 0 , w = 0; char ch = ' ';
    while ( !isdigit(ch) ) w |= ch=='-' , ch = getchar();
    while ( isdigit(ch) ) x = x*10 + ch-48 , ch = getchar();
    return w ? -x : x;
}
int temp[N],t1[N],t2[N],rev[N];
inline void calcrev(int len,int cnt)
{
    for (int i=0;i<len;i++)
        rev[i] = ( rev[i>>1] >> 1 ) | ( (i&1) << (cnt-1) );
}
inline void NTT(int *p,int op,int len)
{
    for (int i=0;i<len;i++)
        if ( i < rev[i] ) swap( p[i] , p[rev[i]] );
    for (int i=1;i<len;i<<=1)
    {
        int w = quickpow( op == 1 ? G : INVG , (Mod-1) / (i<<1) );
        for (int j=0;j<len;j+=i<<1)
        {
            int omega = 1;
            for (int k=0;k<i;k++)
            {
                int x = p[j+k] , y = mul( omega , p[i+j+k] );
                p[j+k] = add( x , y ) , p[i+j+k] = sub( x , y );
                Mul( omega , w );
            }
        }
    }
    if ( op == -1 )
    {
        int val = inv( len );
        for (int i=0;i<len;i++)
            Mul( p[i] , val );
    }
}
inline void Polyinv(int *a,int *b,int n)
{
    int p = 1; while ( p < n ) p <<= 1;
    for (int i=0;i<p;i++) b[i] = 0;
    b[0] = inv( a[0] );
    for (int len=1,cnt=0;len<=p;len<<=1,cnt++)
    {
        calcrev( len<<1 , cnt+1 );
        for (int i=0;i<len;i++) temp[i] = a[i];
        for (int i=len;i<len<<1;i++) temp[i] = 0;
        NTT( b , 1 , len<<1 ) , NTT( temp , 1 , len<<1 );
        for (int i=0;i<len<<1;i++)
            Mul( b[i] , sub( 2 , mul( temp[i] , b[i] ) ) );
        NTT( b , -1 , len<<1 );
        for (int i=len;i<len<<1;i++) b[i] = 0;
    }
    for (int i=n;i<p;i++) b[i] = 0;
}
inline void Polymul(int *a,int *b,int *c,int len)
{
    NTT( a , 1 , len ) , NTT( b , 1 , len );
    for (int i=0;i<len;i++)
        c[i] = mul( a[i] , b[i] );
    NTT( c , -1 , len );
}
inline void Polydiv(int *f,int *g,int *q,int *r,int n,int m)
{
    int len = 1 , cnt = 0;
    while ( len < 2*(n-m+1) ) len <<= 1 , cnt++;
    for (int i=0;i<n-m+1;i++) t2[i] = g[m-i-1];
    for (int i=n-m+1;i<len;i++) t2[i] = 0;
    Polyinv( t2 , t1 , n-m+1 );
    for (int i=0;i<n-m+1;i++) t2[i] = f[n-i-1];
    for (int i=n-m+1;i<len;i++) t2[i] = 0;
    calcrev( len , cnt ) , Polymul( t1 , t2 , q , len );
    reverse( q , q+n-m+1 );

    len = 1 , cnt = 0;
    while ( len < n ) len <<= 1 , cnt++;
    for (int i=0;i<n-m+1;i++) t1[i] = q[i];
    for (int i=n-m+1;i<len;i++) t1[i] = 0;
    for (int i=0;i<m;i++) t2[i] = g[i];
    for (int i=m;i<len;i++) t2[i] = 0;
    calcrev( len , cnt ) , Polymul( t1 , t2 , r , len );
    for (int i=0;i<len;i++)
        r[i] = sub( f[i] , r[i] );
}
int n,m,f[N],g[N],q[N],r[N];
int main(void)
{
    n = read() , m = read();
    for (int i=0;i<n;i++) f[i] = read();
    for (int i=0;i<m;i++) g[i] = read();
    Polydiv( f , g , q , r , n , m );
    for (int i=0;i<n-m+1;i++)
        printf("%d ",q[i]);
    puts("");
    for (int i=0;i<m-1;i++)
        printf("%d ",r[i]);
    puts("");
    return 0;
}


『基础多项式算法总结』

原文地址:https://www.cnblogs.com/Parsnip/p/11421041.html

时间: 2024-09-30 11:19:00

『基础多项式算法总结』的相关文章

『基础同余和费马小定理』

同余 同余是数论中一个重要的概念,若整数\(a\)与整数\(b\)除以正整数\(m\)的余数相等,则称\(a\),\(b\)再模\(m\)意义下同余,记为\(a\equiv b(mod\ m)\)或\(m|(a-b)\). 同余基础性质 \(1.\)\(a≡a (mod\ m)\),自反性 \(2.\)若\(a≡b (mod\ m)\),则\(b≡a (mod\ m)\),对称性 \(3.\)若\(a≡b (mod\ m)\),\(b≡c (mod\ m)\),则\(a≡c (mod\ m)\)

『浅入浅出』MySQL 和 InnoDB

作为一名开发人员,在日常的工作中会难以避免地接触到数据库,无论是基于文件的 sqlite 还是工程上使用非常广泛的 MySQL.PostgreSQL,但是一直以来也没有对数据库有一个非常清晰并且成体系的认知,所以最近两个月的时间看了几本数据库相关的书籍并且阅读了 MySQL 的官方文档,希望对各位了解数据库的.不了解数据库的有所帮助. 本文中对于数据库的介绍以及研究都是在 MySQL 上进行的,如果涉及到了其他数据库的内容或者实现会在文中单独指出. 数据库的定义 很多开发者在最开始时其实都对数据

重新学习MySQL数据库2:『浅入浅出』MySQL 和 InnoDB

重新学习Mysql数据库2:『浅入浅出』MySQL 和 InnoDB 作为一名开发人员,在日常的工作中会难以避免地接触到数据库,无论是基于文件的 sqlite 还是工程上使用非常广泛的 MySQL.PostgreSQL,但是一直以来也没有对数据库有一个非常清晰并且成体系的认知,所以最近两个月的时间看了几本数据库相关的书籍并且阅读了 MySQL 的官方文档,希望对各位了解数据库的.不了解数据库的有所帮助. 本文中对于数据库的介绍以及研究都是在 MySQL 上进行的,如果涉及到了其他数据库的内容或者

『后缀自动机入门 SuffixAutomaton』

本文的图片材料多数来自\(\mathrm{hihocoder}\)中详尽的\(SAM\)介绍,文字总结为原创内容. 确定性有限状态自动机 DFA 首先我们要定义确定性有限状态自动机\(\mathrm{DFA}\),一个有限状态自动机可以用一个五元组\((\mathrm{S},\Sigma,\mathrm{st},\mathrm{end},\delta)\)表示,他们的含义如下: \(1.\) \(\mathrm{S}\) 代表自动机的状态集 \(2.\) \(\Sigma\) 代表字符集,也称字

NHibernate框架与BLL+DAL+Model+Controller+UI 多层架构十分相似--『Spring.NET+NHibernate+泛型』概述、知识准备及介绍(一)

原文://http://blog.csdn.net/wb09100310/article/details/47271555 1. 概述 搭建了Spring.NET+NHibernate的一个数据查询系统.之前没用过这两个框架,也算是先学现买,在做完设计之 后花了一周搭建成功了.其中,还加上了我的一些改进思想,把DAO和BLL之中相似且常用的增删改查通过泛型T抽象到了DAO和BLL的父类中,其DAO 和BLL子类只需继承父类就拥有了这些方法.和之前的一个数据库表(视图)对应一个实体,一个实体对应一

十大基础实用算法之动态规划

动态规划(Dynamic programming)是一种在数学.计算机科学和经济学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法. 动态规划常常适用于有重叠子问题和最优子结构性质的问题,动态规划方法所耗时间往往远少于朴素解法. 动态规划背后的基本思想非常简单.大致上,若要解一个给定问题,我们需要解其不同部分(即子问题),再合并子问题的解以得出原问题的解. 通常许多子问题非常相似,为此动态规划法试图仅仅解决每个子问题一次,从而减少计算量: 一旦某个给定子问题的解已经算出,则将

『浅入深出』MySQL 中事务的实现

在关系型数据库中,事务的重要性不言而喻,只要对数据库稍有了解的人都知道事务具有 ACID 四个基本属性,而我们不知道的可能就是数据库是如何实现这四个属性的:在这篇文章中,我们将对事务的实现进行分析,尝试理解数据库是如何实现事务的,当然我们也会在文章中简单对 MySQL 中对 ACID 的实现进行简单的介绍. 事务其实就是并发控制的基本单位:相信我们都知道,事务是一个序列操作,其中的操作要么都执行,要么都不执行,它是一个不可分割的工作单位:数据库事务的 ACID 四大特性是事务的基础,了解了 AC

『扩欧简单运用』

扩展欧几里得算法 顾名思义,扩欧就是扩展欧几里得算法,那么我们先来简单地回顾一下这个经典数论算法. 对于形如\(ax+by=c\)的不定方程,扩展欧几里得算法可以在\(O(5log_{10}\min\{a,b\})\)的时间内找到该方程的一组特解,或辅助\(gcd\)判断该方程无解. 对于扩欧的详细讲解,可见『扩展欧几里得算法 Extended Euclid』. 那么我们注意到一个问题,扩展欧几里得算法求的只是一组特解.事实上,我们可以根据如下公式得到不定方程的通解: \[ \begin{cas

『概率和数学期望』

概率 基础概念 定义 设样本空间为\(\Omega\),若对于\(\Omega\)中的每一个随机事件\(A\),都存在实值函数\(P(A)\),满足: \(1.\) \(P(A)\geq0\) \(2.\) \(P(\Omega)=1\) \(3.\) 对于若干个两两互斥事件\(A_1,A_2,...,A_n\),有\(\sum_{i=1}^n P(A_i)=P(\bigcup_{i=1}^n A_i)\) 则称\(P(A)\)为随机事件\(A\)发生的概率. 必然事件 一定发生的事件称为必然事