bzoj5300: [Cqoi2018]九连环【FFT+高精度】

本文介绍了一种使用快速傅立叶变换(FFT)结合高精度计算的方法来求解一个特殊的数列问题。该数列遵循特定的递推公式,并且可以被高效地通过FFT进行计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

解题思路:

容易发现 fi=fi1+2fi2+1=2fi1+(i%2) f i = f i − 1 + 2 f i − 2 + 1 = 2 f i − 1 + ( i % 2 ) ,但还不够。
又发现 fi=2i+13 f i = ⌊ 2 i + 1 3 ⌋ ,直接FFT+高精度即可。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int getint()
{
    int i=0,f=1;char c;
    for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
    if(c=='-')c=getchar(),f=-1;
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}
const int N=100005,mod=998244353,g=3;
ll Pow(ll x,int y)
{
    ll res=1;
    for(;y;y>>=1,x=x*x%mod)
        if(y&1)res=res*x%mod;
    return res;
}
ll w[N],w_inv[N],pos[N];
struct Bignum
{
    ll a[N],deg;
    inline void clear(){for(int i=0;i<=deg;i++)a[i]=0;deg=0;}
    inline void copy(const Bignum &A)
    {
        clear();deg=A.deg;
        for(int i=0;i<=deg;i++)a[i]=A.a[i];
    }
    inline void W(){for(int i=deg;i>=0;i--)putchar('0'+a[i]);}
}A,B,C,D,res,res1;
void NTT(ll *f,int len,int on)
{
    for(int i=1;i<len;i++)
        pos[i]=(i&1)?pos[i>>1]>>1|(len>>1):pos[i>>1]>>1;
    for(int i=1;i<len;i++)
        if(i<pos[i])swap(f[i],f[pos[i]]);
    for(int i=1,num=1;i<len;i<<=1,num++)
    {
        ll wi=(on==1?w[num]:w_inv[num]);
        for(int j=0;j<len;j+=(i<<1))
        {
            ll wn=1;
            for(int k=j;k<j+i;k++)
            {
                ll u=f[k],v=wn*f[k+i]%mod;
                f[k]=(u+v)%mod,f[k+i]=(u-v+mod)%mod;
                wn=wn*wi%mod;
            }
        }
    }
    if(on==-1)
        for(int i=0,inv=Pow(len,mod-2);i<len;i++)
            f[i]=f[i]*inv%mod;
}
inline Bignum operator * (const Bignum &A,const Bignum &B)
{
    C.copy(A),D.copy(B);
    res.clear();res.deg=C.deg+D.deg;
    int len=1;
    while(len<=C.deg+D.deg)len<<=1;
    NTT(C.a,len,1),NTT(D.a,len,1);
    for(int i=0;i<len;i++)C.a[i]*=D.a[i];
    NTT(C.a,len,-1);
    for(int i=0;i<=res.deg;i++)res.a[i]=C.a[i];
    for(int i=0;i<len;i++)C.a[i]=D.a[i]=0;
    for(int i=0;i<=res.deg;i++)if(res.a[i]>=10)res.a[i+1]+=res.a[i]/10,res.a[i]%=10;
    while(res.a[res.deg+1])
    {
        int i=++res.deg;
        if(res.a[i]>=10)res.a[i+1]+=res.a[i]/10,res.a[i]%=10;
    }
    return res;
}
inline Bignum Pow(const Bignum &A,int b)
{
    res1.clear(),res1.a[0]=1,B.copy(A);
    for(;b;b>>=1,B=B*B)
        if(b&1)res1=res1*B;
    return res1;
}
inline Bignum operator /(const Bignum &A,int b)
{
    res.clear(),C.copy(A),res.deg=C.deg;
    for(int i=C.deg,ret=C.a[i];i>=0;i--)
    {
        res.a[i]=ret/b;
        if(i)ret=(ret-res.a[i]*b)*10+C.a[i-1];
    }
    while(!res.a[res.deg])res.deg--;
    return res;
}
void solve(int n)
{
    A.clear();A.a[0]=2;
    A=Pow(A,n+1);
    A=A/3;
    A.W(),putchar('\n');
}
int main()
{
    //freopen("baguenaudier.in","r",stdin);
    //freopen("baguenaudier.out","w",stdout);
    int len=1,num=0;
    while(len<=N+N)
    {
        len<<=1;
        w[++num]=Pow(g,(mod-1)/len);
        w_inv[num]=Pow(w[num],mod-2);
    }
    for(int T=getint();T;T--)
        solve(getint());
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值