zoukankan      html  css  js  c++  java
  • 两个多项式的卷积【NTT】

    题意

    2020计蒜之道决赛:

    给出两个多项式 (A(x))(B(x))

    [A(x)=a_0+a_1x+a_2x^2+a_3x^3+cdots +a_nx^n\ B(x)=b_0+b_1x+b_2x^2+b_3x^3+cdots +b_nx^n ]

    (C(x)) 为上述两个多项式的卷积:

    [C(x)=A(x)B(x)=c_0+c_1x+c_2x^2cdots+c_{2n}x^{2n} ]

    现有 (m) 次操作,每次操作可能查询 (sum_{i=l}^{r}{c_i}) ,也可能修改 (A(x)) 中的某个系数。

    具体如下:

    1 l r:代表查询 (sum_{i=l}^{r}{c_i})

    2 p q:表示把 (A(x))(x^p) 的系数增加 (q)

    (1leq n leq 5000,-10^5leq a_i leq 10^5,-10^5leq b_ileq 10^5,0leq pleq n,-10^5leq q leq 10^5)

    输出结果对 (998244353) 取模。

    分析

    对于某段区间的查询,可以转化为对两个前缀和的查询。但对于第二种操作,如果每次修改之后直接做 (NTT) ,复杂度为 (O(mnlog n))

    对于这两个操作而言,一种是“单次优,但数量太大”,一种是“单次劣,但是不限于操作次数”。我们可以减少 (NTT) 的次数,同时控制第一种算法记录的量不要太大。

    维护一个大小为 (S) 的集合,每次来一个修改就把这次的修改信息记录到集合中,当集合大小增长到一定的阀值,就做一次 (NTT) ,同时把集合清空。而一次查询的答案,等于上次做完 (NTT) 的时候的答案,再加上集合中记录的修改操作对当前查询的影响,通过合理控制 (S) 的大小,可以做到既可以不让集合太大,又可以减小 (NTT) 的次数。

    复杂度为:(O(mS+frac{m}{S}nlog n)),通过均值不等式可知:(S=sqrt{nlog n}) 时,复杂度最优秀。

    代码

    #include <bits/stdc++.h>
    
    using namespace std;
    typedef long long ll;
    const int mod=998244353;
    const int N=5100;
    const int g=3;
    ll A[N<<2],B[N<<2],C[N<<2],pre[N<<2],a[N<<2];
    int rev[N<<2],S[N];
    int pos[N],w[N],num;
    ll power(ll x,ll y)
    {
        ll res=1;
        x%=mod;
        while(y)
        {
            if(y&1)
                res=res*x%mod;
            x=x*x%mod;
            y>>=1;
        }
        return res;
    }
    void NTT(ll *pn,int len,int f)
    {
        for(int i=0;i<len;i++)
            if(i<rev[i]) swap(pn[i],pn[rev[i]]);
        for(int i=1;i<len;i<<=1)
        {
            ll wn=power(g,1LL*(mod-1)/(2LL*i));
            if(f==-1) wn=power(wn,mod-2);
            for(int j=0,d=(i<<1);j<len;j+=d)
            {
                ll w=1;
                for(int k=0;k<i;k++)
                {
                    ll u=pn[j+k],v=w*pn[j+k+i]%mod;
                    pn[j+k]=(u+v)%mod,pn[j+k+i]=((u-v)%mod+mod)%mod;
                    w=wn*w%mod;
                }
            }
        }
        if(f==-1)
        {
            ll inv=power(1LL*len,mod-2);
            for(int i=0;i<len;i++)
                pn[i]=pn[i]*inv%mod;
        }
    }
    void dontt(int len)
    {
        for(int i=0;i<len;i++)
            A[i]=a[i];//因为NTT会改变原有系数的位置和值
        NTT(A,len,1);
        for(int i=0;i<len;i++)
            C[i]=A[i]*B[i]%mod;
        NTT(C,len,-1);
        for(int i=1;i<len;i++)
            C[i]=(C[i-1]+C[i])%mod;
    }
    ll cal(int x)
    {
        if(x<0) return 0;
        ll res=C[x];
        for(int i=1;i<=num;i++)
        {
            int t=x-pos[i];
            if(t>=0)//确定B需要的前缀和最大下标
                res=(res+(pre[t]*w[i]%mod)+mod)%mod;
        }
        return res;
    }
    int main()
    {
        int n,m,op,x,y;
        scanf("%d",&n);
        for(int i=0;i<=n;i++)
        {
            scanf("%lld",&a[i]);
            a[i]=(a[i]+mod)%mod;
        }
        for(int i=0;i<=n;i++)
        {
            scanf("%lld",&B[i]);
            B[i]=(B[i]+mod)%mod;
            if(i==0)
                pre[i]=B[i];
            else
                pre[i]=(pre[i-1]+B[i])%mod;
        }
        int cnt=0,len=1;
        while(len<=2*n)//注意len不要开小了
        {
            len<<=1;
            cnt++;
        }
        for(int i=n+1;i<len;i++)
            pre[i]=(pre[i-1]+B[i])%mod;
        for(int i=0;i<len;i++)
            rev[i]=(rev[i>>1]>>1)|((i&1)<<(cnt-1));
        NTT(B,len,1);
        dontt(len);
        int mx=(int)sqrt(1.0*n*log2(1.0*n));
        num=0;
        scanf("%d",&m);
        while(m--)
        {
            scanf("%d%d%d",&op,&x,&y);
            if(op==1)
                printf("%lld
    ",(cal(y)-cal(x-1)+mod)%mod);
            else
            {
                pos[++num]=x;//记录修改的位置
                a[x]=(a[x]+y+mod)%mod;
                w[num]=y;//
                if(num>=mx)
                {
                    dontt(len);
                    num=0;
                }
            }
        }
        return 0;
    }
    
  • 相关阅读:
    MySQL单机多实例安装并配置主从复制
    linux编译安装MySQL
    Mysql的float类型造成的困扰总结
    kettle初探
    最近的工作总结
    【spring boot】SpringBoot初学(7)– 多数据源及其事务
    【spring boot】SpringBoot初学(6)– aop与自定义注解
    【spring boot】SpringBoot初学(5)– WebService之Jersey
    SpringBoot初学(4)– JdbcTemplate和Mybatis
    【spring boot】SpringBoot初学(3)– application配置和profile隔离配置
  • 原文地址:https://www.cnblogs.com/1024-xzx/p/13984134.html
Copyright © 2011-2022 走看看