前言

学习了Great_Influence的递推实现,我给大家说一下多项式求逆严格的边界条件,因为我发现改动一些很小的边界条件都会使程序出错。怎么办,背代码吗?背代码是不可能,这辈子都不会背代码的。理解了边界条件就不会出错了。

分析

理论基础
\[A \times B \equiv 1 \qquad (\mod{x^n})\]
\[A \times B' \equiv 1 \qquad (\mod{x^{\frac{n}{2}}})\]
\[A \times (B-B') \equiv 0 \qquad (\mod{x^{\frac{n}{2}}})\]
\[B-B' \equiv 0 \qquad(\mod{x^{\frac{n}{2}}})\]
\[(B-B')^2 \equiv 0 \qquad(\mod{x^n})\]
\[B^2-2BB'+B'^2 \equiv 0 \qquad (\mod{x^n})\]
\[A(B^2-2BB'+B'^2) \equiv 0 \qquad (\mod{x^n})\]
\[B-2B'+AB'^2 \equiv 0 \qquad (\mod{x^n})\]
\[B \equiv 2B'-AB'^2 \qquad (\mod{x^n})\]

根据这个式子就可以倍增求多项式逆元了。但是如何倍增呢?或许你已兴冲冲地打出了NTT的板子,然后感觉无从下手。

代码

前置是NTT

inline void FFT(int*t,int lim,int type)
{
    for(rg int i=0;i<lim;++i)
        if(i<rev[i])
            swap(t[i],t[rev[i]]);
    for(rg int i=1;i<lim;i<<=1)
    {
        int gn=qpow(g,(mod-1)/(i<<1));
        if(type==-1)
            gn=qpow(gn,mod-2);
        for(rg int j=0;j<lim;j+=(i<<1))
        {
            int gi=1;
            for(rg int k=0;k<i;++k,gi=(ll)gi*gn%mod)
            {
                int x=t[j+k],y=(ll)gi*t[j+i+k]%mod;
                t[j+k]=module(x,y);
                t[j+i+k]=module(x,mod-y);
            }
        }
    }
    if(type==-1)
    {
        int inv=qpow(lim,mod-2);
        for(rg int i=0;i<lim;++i)
            t[i]=(ll)t[i]*inv%mod;
    }
}
  • 为什么i从1开始小于lim?因为i是下层长度,这也是qpow里面i要乘2的原因。
  • 为什么j从0开始小于lim?因为j是当前层的起始位置,而数组是base 0的。
  • 为什么k从0开始小于i?因为k是当前合并区间的下标,为方便计算从0开始小于i。

首先要封装多项式柯西乘法(卷积),减少代码量以及出错的可能性。

int X[MAXN],Y[MAXN];

inline void mul(int*x,int*y,int lim)
{
    memset(X,0,sizeof(X));
    memset(Y,0,sizeof(Y));
    for(rg int i=0;i<(lim>>1);++i) // edit 2 lim>>1
        X[i]=x[i],Y[i]=y[i];
    FFT(X,lim,1);
    FFT(Y,lim,1);
    for(rg int i=0;i<lim;++i)
        X[i]=(ll)X[i]*Y[i]%mod;
    FFT(X,lim,-1);
    for(rg int i=0;i<lim;++i)
        x[i]=X[i];
}
  • 为什么要把数组复制到XY上?因为避免自乘出错,自己乘自己会导致NTT了两次。

  • 为什么第一层for循环边界为i<lim/2?因为乘出来度数是lim,乘之前度数是lim/2.

然后实现三个辅助函数,分别是 快速幂、 快速模加 和 计算rev 。

inline int qpow(int x,int k)
{
    int ans=1;
    while(k)
    {
        if(k&1)
            ans=(ll)ans*x%mod;
        x=(ll)x*x%mod,k>>=1;
    }
    return ans;
}

inline int module(int x,int y)
{
    x+=y;
    if(x>=mod)
        x-=mod;
    return x;
}

int rev[MAXN];

inline void calrev(int lim,int l)
{
    for(rg int i=1;i<lim;++i)
        rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1));
}
  • 这三个东西的作用不用细说,主要是计算rev数组下表i可以从1开始的原因是rev[0]恒等于0.

最后就是主菜求逆元了,可以用滚动数组减少空间

int a[MAXN],b[2][MAXN];

int main()
{
//  freopen(".in","r",stdin);
//  freopen(".out","w",stdout);
    read(n);
    --n;
    for(rg int i=0;i<=n;++i)
        read(a[i]);
    int cur=0;
    b[cur][0]=qpow(a[0],mod-2);
    int bas=1,lim=2,len=1; // bas:下层长度(当前计算的) lim:上层长度(计算出来的) len:log_2(lim)
    calrev(lim,len);
    while(bas<=(n<<1)) // edit 1 <=
    {
        cur^=1;
        memset(b[cur],0,sizeof(b[cur]));
        for(int i=0;i<bas;++i)
            b[cur][i]=module(b[cur^1][i]<<1,0);
        mul(b[cur^1],b[cur^1],lim);
        mul(b[cur^1],a,lim);
        for(int i=0;i<bas;++i)
            b[cur][i]=module(b[cur][i],mod-b[cur^1][i]);
        bas<<=1,lim<<=1,++len;
        if(bas<=(n<<1))
            calrev(lim,len);
    }
    for(rg int i=0;i<=n;++i)
        printf("%d ",b[cur][i]);
//  fclose(stdin);
//  fclose(stdout);
    return 0;
}
  • 由于这道题就是裸的求逆元,所以我就没封装求逆。其实封装也很简单,加个void inv(int*a,int**b,int&cur,int n)就行了cur要引用是因为需要知道算出来的结果是b数组的哪个
  • 为什么第一个for循环边界是bas,第二个是lim?因为长度倍增了。另外mul里面也得用lim也是这个原因。
  • 为什么bas<=2*n?因为...当前层的长度得覆盖、大于n...吗?其实不是。

为什么bas<=2*n?

考虑我们的数组范围。长度看似倍增了,实则不然。数组下标为0~bas-1,代表\(\sum_{i=0}^{bas-1}a_ix^i\)的各项系数,然而自乘之后最高项次数应变为2(bas-1)而不是程序里面认为的2bas-1,所以我们求出来的多项式其实有虚假成分。怎么处理?程序里面是不好更改的,没必要为此增加代码量和出错性。那么求大一点就好了,比如求到2*n,这样虚假成分不会影响到最终答案。

LG4238 【【模板】多项式求逆】的更多相关文章

  1. 洛谷.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\ ...

  2. 洛谷 P4238 [模板] 多项式求逆

    题目:https://www.luogu.org/problemnew/show/P4238 看博客:https://www.cnblogs.com/xiefengze1/p/9107752.html ...

  3. [模板][P4238]多项式求逆

    NTT多项式求逆模板,详见代码 #include <map> #include <set> #include <stack> #include <cmath& ...

  4. 2018.12.30 洛谷P4238 【模板】多项式求逆

    传送门 多项式求逆模板题. 简单讲讲? 多项式求逆 定义: 对于一个多项式A(x)A(x)A(x),如果存在一个多项式B(x)B(x)B(x),满足B(x)B(x)B(x)的次数小于等于A(x)A(x ...

  5. luogu P4725 多项式对数函数 (模板题、FFT、多项式求逆、求导和积分)

    手动博客搬家: 本文发表于20181125 13:25:03, 原地址https://blog.csdn.net/suncongbo/article/details/84487306 题目链接: ht ...

  6. P4238 【模板】多项式求逆

    思路 多项式求逆就是对于一个多项式\(A(x)\),求一个多项式\(B(x)\),使得\(A(x)B(x) \equiv 1 \ (mod x^n)\) 假设现在多项式只有一项,显然\(B(x)\)的 ...

  7. FFT模板 生成函数 原根 多项式求逆 多项式开根

    FFT #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> ...

  8. [洛谷P4238]【模板】多项式求逆

    题目大意:多项式求逆 题解:$ A^{-1}(x) = (2 - B(x) * A(x)) \times B(x) \pmod{x^n} $ ($B(x)$ 为$A(x)$在$x^{\lceil \d ...

  9. 洛谷P4721 【模板】分治 FFT(生成函数+多项式求逆)

    传送门 我是用多项式求逆做的因为分治FFT看不懂…… upd:分治FFT的看这里 话说这个万恶的生成函数到底是什么东西…… 我们令$F(x)=\sum_{i=0}^\infty f_ix^i,G(x) ...

随机推荐

  1. Django 前台通过json 取出后台数据

    转载自:https://my.oschina.net/esdn/blog/814111 步骤1:后台数据通过 JSON 序列化成字符串 注意:1.json是1个字符串 2.通过json.dumps(' ...

  2. JavaScript -基础- 变量、常量

    一.变量 <script> var a=1 var b=3 var a= 1;   //使用var 定义变量,分号结尾(可不加,换行符也可) var b=3; var a= 1; var ...

  3. win10企业版激活

    slmgr.vbs /upk slmgr /ipk NPPR9-FWDCX-D2C8J-H872K-2YT43 slmgr /skms zh.us.to slmgr /ato

  4. Oracle与MySQL的SQL语句区别

    2 表 2.1 创建表(同) create table tableName( columnName1 int, columnName2 int ) 2.2 删除表(异) MySQL: drop tab ...

  5. bind配置文件

    options{} - 整个bind使用的全局配置选项 bind监听的端口,数据文件存储位置,缓存的存储位置,权限加密的控制 logging{}- 服务日志选项 日志输出的位置,以及输出的级别 zon ...

  6. DevExpress WinForms v18.2新版亮点(一)

    行业领先的.NET界面控件2018年第二次重大更新——DevExpress v18.2日前正式发布,本站将以连载的形式为大家介绍各版本新增内容.本文将介绍了DevExpress WinForms v1 ...

  7. 给定两个数组,这两个数组是排序好的,让你求这两个数组合到一起之后第K大的数。

    题目:给定两个数组,这两个数组是排序好的,让你求这两个数组合到一起之后第K大的数. 解题思路: 首先取得数组a的中位数a[aMid],然后在b中二分查找a[aMid],得到b[bMid],b[bSt] ...

  8. synchronized(六)

    package com.bjsxt.base.sync006;/** * 锁对象的改变问题 * @author alienware * */public class ChangeLock { priv ...

  9. php优秀框架codeigniter学习系列——安装,配置

    下载 可在官网下载,我使用的是CodeIgniter-3.1.7. 目录 打开程序目录,可看到目录结构. 我这里做一点小的修改,新建了一个 index 目录,将 index.php 和一些静态文件放入 ...

  10. ChinaCock界面控件介绍-CCGridPictureEditor

    CCGridPictureEditor如其名,网格图片编辑控件,实现利用一个网格来显示多张图片的缩略图,这是一个非常实用的控件,实现类似微信朋友圈中发布多张图片的功能. 在没有这个控件之前,我都是用D ...