模拟赛 斯特林数计数 题解(拉格朗日插值)

题目大意:

模拟赛 斯特林数计数 题解(拉格朗日插值)_第1张图片

答案对\(10^9 + 7\)取模。

首先,我们发现这个东西出现了很多次。
设R=

R可以矩阵乘法求。
根据斯特林数的

原式可化为
设所求为\(f_k(N)\)

对于\(R=1\)的情况,自然数幂求和即可。\(O(k+logn)\)
模拟赛 斯特林数计数 题解(拉格朗日插值)_第2张图片
所以,

由上述暴力展开的情况可以发现,存在一个关于\(N\)\(m\)多项式\(F_m(N)\)
使得\(f_m(N)=F_m(N)R^{N+1}-F_m(0)R\),利用上述的展开方式也可归纳证明这个结论。

所以,求出\(F_m(N)\)即可,由于它是\(m\)次多项式,所以,求出\(F_m(0)到F_m(m)\)即可插值求出\(F_m(N)\)
因为\(f_m(N)-f_m(N-1)=F_m(N)R^{N+1}-F_m(N-1)R^N=N^m R^N\),所以\(F_m(N)R-F_m(N-1)=N^m\)
\(F_m(N)=\frac{N^m+F_m(N-1)}{R}\)
\(F_m(0)=x\),那么\(F_m(i)\)就能表示为\(k_ix+b_i\)的形式。
那么,我们只要再找一条\(F_m\)的等量关系即可求出。
模拟赛 斯特林数计数 题解(拉格朗日插值)_第3张图片

代入到上一条式子,即可得出

(其实就是说m次多项式的m+1次差分等于0,这条式子可以保证\(F_m\)形成一个m次多项式)。
这样,我们就能求出\(F_m(0)到F_m(m)\),再通过插值,即可求出\(F_m(N)\),进而求出答案。

通过线性筛,可以在计算质数\(p\)\(p^m\)后线性地算出所有正整数\(x\)\(x^m\)
时间复杂度为\(O(\frac{m}{ln(m)}*log_2(m))=O(m)\)

总时间复杂度\(O(k+logn)\)

代码:

#include 
#define md 1000000007
#define ll long long
int ny[200010],jc[200010],jn[200010];
int C(int n,int m)
{
    return 1ll*jc[n]*jn[m]%md*jn[n-m]%md;
}
int ksm(int a,int b)
{
    int jg=1;
    while(b>0)
    {
        if(b&1)
            jg=1ll*jg*a%md;
        a=1ll*a*a%md;
        b=(b>>1);
    }
    return jg;
}
int getfib(ll x)
{
    int a=1,b=0,c=0,d=1,w[70],s=0;
    while(x>0)
    {
        w[s++]=x%2;
        x/=2;
    }
    for(int i=s-1;i>=0;i--)
    {
        int bc=1ll*b*c%md,ad=(a+d)%md;
        b=1ll*b*ad%md;c=1ll*c*ad%md;
        a=(1ll*a*a+bc)%md;
        d=(1ll*d*d+bc)%md;
        if(w[i])
        {
            int ta=a,tc=c;
            a=(a+b)%md;
            c=(c+d)%md;
            b=ta;d=tc;
        }
    }
    return c;
}
int qz[200010],hz[200010];
int jisuan(int sz[200010],int n,ll x)
{
    for(int i=0;i<=n;i++)
    {
        if(i==0)qz[i]=1;
        else qz[i]=qz[i-1];
        qz[i]=1ll*qz[i]*(x%md-i+md)%md;
    }
    for(int i=n;i>=0;i--)
    {
        if(i==n)hz[i]=1;
        else hz[i]=hz[i+1];
        hz[i]=1ll*hz[i]*(x%md-i+md)%md;
    }
    int ans=0;
    for(int i=0;i<=n;i++)
    {
        int l=1ll*jn[i]*jn[n-i]%md;
        if(i>0)l=1ll*l*qz[i-1]%md;
        if(ix)
                break;
            sa[i*ss[j]]=true;
            mi[i*ss[j]]=1ll*mi[i]*mi[ss[j]]%md;
            if(i%ss[j]==0)break;
        }
    }
}
int main()
{
    freopen("c.in","r",stdin);
    freopen("c.out","w",stdout);
    ll n,r;int K;
    scanf("%lld%lld%d",&n,&r,&K);
    ny[1]=jc[0]=jn[0]=1;
    for(int i=2;i<=K+1;i++)
        ny[i]=1ll*(md-md/i)*ny[md%i]%md;
    for(int i=1;i<=K+1;i++)
    {
        jc[i]=1ll*jc[i-1]*i%md;
        jn[i]=1ll*jn[i-1]*ny[i]%md;
    }
    yucl(K+1,K);
    int R=(getfib(r+2)-1+md)%md;
    if(R==1)
    {
        for(int i=1;i<=K+1;i++)
            mi[i]=(mi[i]+mi[i-1])%md;
        printf("%d",jisuan(mi,K+1,n));
    }
    else
    {
        int rn=ksm(R,md-2);
        k[0]=1;b[0]=0;
        for(int i=1;i<=K+1;i++)
        {
            k[i]=1ll*k[i-1]*rn%md;
            b[i]=1ll*(b[i-1]+mi[i])*rn%md;
        }
        int hk=0,hb=0;
        for(int i=0;i<=K+1;i++)
        {
            int t=C(K+1,i);
            if(i%2==1)
                t=1ll*t*(md-1)%md;
            hk=(hk+1ll*t*k[i])%md;
            hb=(hb+1ll*t*b[i])%md;
        }
        F[0]=1ll*(md-hb)*ksm(hk,md-2)%md;
        for(int i=1;i<=K;i++)
            F[i]=(1ll*k[i]*F[0]+b[i])%md;
        int fn=jisuan(F,K,n);
        int ans=1ll*fn*ksm(R,(n+1)%(md-1))%md;
        ans=(ans-1ll*F[0]*R%md+md)%md;
        printf("%d\n",ans);
    }
    fclose(stdin);
    fclose(stdout);
    return 0;
}

你可能感兴趣的:(模拟赛 斯特林数计数 题解(拉格朗日插值))