1. 程式人生 > >BZOJ 4555:[TJOI2016&HEOI2016]求和(第二類斯特林數+NTT)

BZOJ 4555:[TJOI2016&HEOI2016]求和(第二類斯特林數+NTT)

pla scrip sca getc += math scan 直接 main

\(Description\)

\[\sum_{i=0}^n\sum_{j=0}^iS(i,j)2^jj!\]對998244353取模後的結果。
\(S(i,j)\)在這裏就非常礙事,怎麽把它寫成一個多項式的形式呢?
第二類斯特林數含有一種容斥的寫法
\[S(n,m)=\frac{1}{m!}\sum_{i=0}^m(-1)^iC_m^i(m-i)^n\]
把它帶到要求的式子裏去
\[\sum_{i=0}^n\sum_{j=0}^i2^jj!\frac{1}{j!}\sum_{k=0}^j(-1)^k\frac{j!}{k!(j-k)!}(j-k)^i\]
\[=\sum_{j=0}^n2^jj!\sum_{k=0}^j\frac{(-1)^k}{k!}\frac{\sum_{i=0}^n(j-k)^i}{(j-k)!}\]


最後是個等比數列求和
\[\sum_{j=0}^n2^jj!\sum_{k=0}^j\frac{(-1)^k}{k!}\frac{(j-k)^{n+1}-1}{(j-k-1)(j-k)!}\]
後邊的求和直接\(NTT\)做,就好了。

#include<complex>
#include<cstdio>
using namespace std;
const int mod=998244353,R=3;
const int N=3e5+7;
int n,invR;
int F[N],G[N],fac[N],finv[N],r[N];
int qread()
{
    int x=0;
    char ch=getchar();
    while(ch<'0' || ch>'9')ch=getchar();
    while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x;
}
int Fpow(long long b,int p)
{
    long long res=1;
    for(;p;p>>=1,b=b*b%mod)
        if(p&1)res=res*b%mod;
    return res;
}
void NTT(int *a,int lim,int opt)
{
    for(int i=1;i<lim;i++)
        if(i<r[i])swap(a[i],a[r[i]]);
    for(int i=2;i<=lim;i<<=1)
    {
        int mid=i>>1,Wn=Fpow(~opt?R:invR,(mod-1)/i),t;
        for(int j=0;j<lim;j+=i)
        {
            long long w=1;
            for(int k=j;k<j+mid;k++,w=w*Wn%mod)
            {
                t=1ll*w*a[k+mid]%mod;
                a[k+mid]=(a[k]-t+mod)%mod;a[k]=(a[k]+t)%mod;
            }
        }
    }
    if(opt==-1)for(int i=0,inv=Fpow(lim,mod-2);i<lim;i++)a[i]=1ll*a[i]*inv%mod;
}
int main()
{
    scanf("%d",&n);
    fac[0]=finv[0]=1;
    for(int i=1;i<=n;i++)
        fac[i]=1ll*fac[i-1]*i%mod;
    finv[n]=Fpow(fac[n],mod-2);
    for(int i=n-1;i;i--)
        finv[i]=1ll*finv[i+1]*(i+1)%mod;
    for(int i=2;i<=n;i++)
        F[i]=1ll*(Fpow(i,n+1)-1)*Fpow(i-1,mod-2)%mod*finv[i]%mod;
    F[0]=1;F[1]=n+1;
    for(int i=0;i<=n;i++)
        G[i]=((i&1?-1:1)*finv[i]+mod)%mod;
    int lim=1,l=-1;
    invR=Fpow(R,mod-2);
    while(lim<=n+n)lim<<=1,l++;
    for(int i=1;i<lim;i++)r[i]=(r[i>>1]>>1)|((i&1)<<l);
    NTT(F,lim,1);NTT(G,lim,1);
    for(int i=0;i<lim;i++)
        F[i]=1ll*F[i]*G[i]%mod;
    NTT(F,lim,-1);
    int ans=0;
    for(int i=0,p=1;i<=n;i++,p=(p<<1)%mod)
        ans=(ans+1ll*p*fac[i]%mod*F[i]%mod)%mod;
    printf("%d\n",ans);
    return 0;
}

BZOJ 4555:[TJOI2016&HEOI2016]求和(第二類斯特林數+NTT)