牛客挑战赛14-F细胞

https://www.nowcoder.com/acm/contest/81/F

循环卷积的裸题,太久没做FFT了,这么裸的循环卷积都看不出来

注意一下本文的mod 都是指表示幂的模数,而不是NTT用到的模数

  • 首先我们先不管m,考虑多项式

可以发现这个是一个多项式的n次幂,正常求一个多项式的n次幂,可以用快速幂套NTT,复杂度n*log(n)*log(n), 最多只能做n在1e4左右的题。

  • 现在在来考虑m,则原式为。

      显然这就是循环卷积的常见形式

    如果先用快速幂套NTT 把多项式系数算出来a[i], 再对i%mod同余的系数进行累加,时间和空间都是会爆炸的。

  不过在多项式快速幂实现的时候不难发现,可以每做一次多项式乘法,就对幂取余一次,合并幂的余数相同的项。这样空间可以降到2*mod, 时间复杂度    mod*log(mod)*log(n)      但是这样还是会超时的

  • 最后重点来了,上面是一般的情况的下循环卷积的做法,循环卷积还有一种特殊情况,就是指数的mod=2^m 时,这时循环卷积可以直接变成频域上的2^m-1 次多项式的点乘(注意要系数等于2^m-1 的点乘, 不需要先以前一样开两倍大小,以防止多项式系数溢出,这里就是要溢出才能保证正确性),这时可以发现,NTT 前可NTT后都是mod-1次多项,没有系数合并的那一步,所以干脆中间乘的时候,就不要NTT回来,  直接在点成的时候做快速幂。 这样只需做一次NTT和逆NTT.时间复杂度  mod*(log(n)+log(mod))
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
typedef long long ll;
#define N 2000005
ll a[N],b[N];
const ll PMOD=998244353;
const ll PR=3;
static ll qp[30];
ll res[N];
struct NTT__container
{
    NTT__container()
    {
        int  t,i;
        for( i=0; i<21; i++)///注意循环上界与2n次幂上界相同
        {
            t=1<<i;
            qp[i]=quick_pow(PR,(PMOD-1)/t);
        }
    }
    ll quick_pow(ll x,ll n)
    {
        ll ans=1;
        while(n)
        {
            if(n&1)
                ans=ans*x%PMOD;
            x=x*x%PMOD;
            n>>=1;
        }
        return ans;
    }
    int get_len(int n)///计算刚好比n大的2的N次幂
    {
        int i,len;
        for(i=(1<<30); i; i>>=1)
        {
            if(n&i)
            {
                len=(i<<2);
                break;
            }
        }
        return len;
    }
    inline void NTT(ll F[],int len,int type)
    {
        int id=0,h,j,k,t,i;
        ll E,u,v;
        for(i=0,t=0; i<len; i++)///逆位置换
        {
            if(i>t)    swap(F[i],F[t]);
            for(j=(len>>1); (t^=j)<j; j>>=1);
        }
        for( h=2; h<=len; h<<=1)///层数
        {
            id++;
            for( j=0; j<len; j+=h)///遍历这层上的结点
            {
                E=1;///旋转因子
                for(int k=j; k<j+h/2; k++)///遍历结点上的前半序列
                {
                    u=F[k];///A[0]
                    v=(E*F[k+h/2])%PMOD;///w*A[1]
                    ///对偶计算
                    F[k]=(u+v)%PMOD;
                    F[k+h/2]=((u-v)%PMOD+PMOD)%PMOD;
                    ///迭代旋转因子
                    E=(E*qp[id])%PMOD;///qp[id]是2^i等分因子
                }
            }
        }
        if(type==-1)
        {
            int i;
            ll inv;
            for(i=1; i<len/2; i++)///转置,因为逆变换时大家互乘了对立点的因子
                swap(F[i],F[len-i]);
            inv=quick_pow(len,PMOD-2);///乘逆元还原
            for( i=0; i<len; i++)
                F[i]=(F[i]%PMOD*inv)%PMOD;
        }
    }
    inline void inv(ll *a,int len)///答案存在res中
    {
        if(len==1)
        {
            res[0]=quick_pow(a[0],PMOD-2);
            return ;
        }
        inv(a,len>>1);///递归
        static ll temp[N];
        memcpy(temp,a,sizeof(ll)*(len>>1));
        NTT(temp,len,1);
        NTT(res,len,1);
        int i;
        for(i=0; i<len; i++)
            res[i]=res[i]*(2-temp[i]*res[i]%PMOD+PMOD)%PMOD;///多项式逆元迭代公式
        NTT(res,len,-1);
        memset(res+(len>>1),0,sizeof(ll)*(len>>1));
    }
    void mul(ll x[],ll y[],int len)///答案存在x中
    {
        int i;
        NTT(x,len,1);///先变换到点值式
        NTT(y,len,1);///先变换到点值式上
        for(i=0; i<len; i++)
            x[i]=(x[i]*y[i])%PMOD;///在点值上点积
        NTT(x,len,-1);///再逆变换回系数式
    }

} cal;
void print(ll a[],int len)
{
    int high=0,i;
    for(i=len-1; i>=0; i--)
    {
        if(a[i])
        {
            high=i;
            break;
        }
    }
    for(i=high; i>=0; i--)putchar(a[i]+'0');
    puts("");
}
int main()
{
    int m,i,j,k,len;
    long long n;
//    printf("%lld\n",PMOD);
    scanf("%lld%d",&n,&m);
    len=1<<m;
    a[0]=1;
    a[1]=2;
    cal.NTT(a,len,1);
    for(i=0;i<len;i++)
    {
        a[i]=cal.quick_pow(a[i],n);
    }
    cal.NTT(a,len,-1);
    long long temp=1,ans=0;
    for(i=0;i<len;i++)
    {
        ans+=temp*a[i]%PMOD;
        temp=temp*2222303%PMOD;
    }
    printf("%lld\n",ans%PMOD);
    return 0;
}
View Code

 

posted @ 2018-06-28 21:10  强势围观  阅读(241)  评论(0编辑  收藏  举报