Codeforces Round #641 Div1.F Slime and Sequences 中文题解

· · 题解

Slime and Sequences 中文题解

UPD:你可以在这里看到 Elegia 的题解:https://blog.csdn.net/EI_Captain/article/details/106122006

Easy Version

首先我们可以将长度为 n 的好序列和长度为 n 的排列一一对应。设一个长度为 n 的排列为 a_1,a_2,\cdots,a_n ,则在每个 a_i,a_{i+1} 之间填入大于号或小于号,则 p_{a_i} 的值即为 a_1,a_2,\cdots ,a_i 之间小于号的数量加 1 ,不难验证这是一个 S 与所有排列的集合的一个一一对应。

d_{i,j} 为在长度为 i+1 的排列中在 a_1,a_2,\cdots ,a_{i+1} 至少有 j 个小于号的方案数,则将每一段连续的小于号当成一段,对确定的数的集合填入这一段中只有唯一一种填法,而 d_{i,j} 中有 i-j+1 段,所以 d_{i,j} 的值即为将 n 个数分成有区别的 i-j +1 组的方案数,由第二类斯特林数的 EGF 可以知道: d_{i,j}=(i+1)![z^{i+1}](e^z-1)^{i-j+1} 。我们也可以用类似第二类斯特林数的dp求出每个 d_{i,j} 的值。

求答案时,对每个 1\leq i\leq n ,我们考虑每个位置上的贡献,即对于每个 1n 的排列,找每个位置前面有 i-1 个小于号的方案数,可以列出式子:

ans_{i+1}=\sum\limits_{x=0}^{n-1}\sum\limits_{y=i}^x (-1)^{y-i} {y\choose i}d_{x,y}\frac{n!}{(x+1)!} =\frac {n!}{i!}\sum\limits_{x=0}^{n-1}\sum\limits_{y=i}^x (-1)^{y-i}\frac{y!d_{x,y}}{(y-i)!(x+1)!} =\frac{n!}{i!} \sum\limits_{y=i}^{n-1}\frac{(-1)^{y-i}y!}{(y-i)!}\sum\limits_{x=y}^{n-1}\frac{d_{x,y}}{(x+1)!} =\frac {n!}{i!}\sum\limits_{y=i}^{n-1} \frac{(-1)^{y-i}y!}{(y-i)!} \sum\limits_{x=y}^{n-1}[z^{x+1}](e^z-1)^{x-y+1}

如果对每个 y 求出了 \sum\limits_{x=y}^{n-1}[z^{x+1}](e^z-1)^{x-y+1} ,即可以用一次卷积求出所有 ans_i 的值。

由于存在简单的 O(n^2) 算法来对每个 y 求出上式的值,所以如果我们用 O(n^2) 的dp求 d_{x,y} ,我们就得到了一个 O(n^2) 的做法,可以通过简单版 (其他形式的dp也可以得到这个复杂度,我们在这里仅给出可以导向正解的做法)。

Hard Version

接下来考虑如何在低于 O(n^2) 的时间内求出这一系列的值。

\sum\limits_{x=y}^{n-1}[z^{x+1}](e^z-1)^{x-y+1}=\sum\limits_{x=y+1}^n[z^x](e^z-1)^{x-y} =[z^y]\sum\limits_{x=y+1}^n(\frac{e^z-1}z)^{x-y}=[z^y]\sum\limits_{x=1}^{n-y}(\frac{e^z-1}z)^x

F=\frac{e^z-1}z ,现在我们想要对每个 0\leq i\leq n-1 ,求出 [z^i]\sum\limits_{k=1}^{n-i}F^k

[z^i]\sum\limits_{k=1}^{n-i}F^k =[z^i]\frac 1{1-F}-[z^i]\frac{F^{n-i+1}}{1-F}

我们可以用一次多项式求逆求出 [z^i]\frac 1{1-F} ,现在只需处理后面的一项。

[z^i]\frac{F^{n-i+1}}{1-F}=[z^{n+1}]\frac{(zF)^{n-i+1}}{1-F}

w(z)=zF(z)\phi (z) 满足 \frac{w(z)}{\phi (w(z))}=z ,则 \frac{zF(z)}{\phi (w(z))}=z ,即 F=\phi (w)

原式 =[z^{n+1}u^{n-i+1}]\sum\limits_{k=0}^\infty \frac{(uzF)^k}{1-F}=[z^{n+1}u^{n-i+1}] \frac 1{1-\phi (w)}\frac 1{1-uw}

由拉格朗日反演可得

=[u^{n-i+1}]\frac 1{n+1} [z^n] ((\frac 1{1-\phi z}\frac 1{1-uz})'\cdot \phi (z)^{n+1}) =\frac 1{n+1} [z^nu^{n-i+1}] (\phi (z)^{n+1}\frac{u+\phi'(z)-u\phi(z)-uz\phi'(z)}{(1-\phi (z))^2(1-uz)^2}) =\frac 1{n+1} [z^nu^{n-i+1}] (\phi(z)^{n+1}(\frac {u}{(1-\phi (z))(1-uz)^2}+\frac {\phi'(z)}{(1-\phi (z))^2(1-uz)})) =\frac 1{n+1}[z^nu^{n-i+1}](\phi (z)^{n+1}(\frac 1{1-\phi(z)}\sum\limits_{k=0}^\infty (k+1)z^ku^{k+1}+\frac{\phi'(z)}{(1-\phi (z))^2}\sum\limits_{k=0}^\infty z^ku^k)) =\frac 1{n+1}[z^n](\phi (z)^{n+1}(\frac{(n-i+1)z^{n-i}}{1-\phi (z)}+\frac{\phi'(z)z^{n-i+1}}{(1-\phi(z))^2})) =\frac 1{n+1}([z^i](\phi(z)^{n+1}\frac{n-i+1}{1-\phi(z)})+[z^{i-1}](\phi(z)^{n+1}\frac{\phi'(z)}{(1-\phi(z))^2}))

我们可以在 O(n\log n)\ \sim \ O(n\log ^2n) 的时间内(取决于你如何求多项式exp)求出 \phi(z)^{n+1}\frac{n-i+1}{1-\phi(z)}\phi(z)^{n+1}\frac{\phi'(z)}{(1-\phi(z))^2} ,从而得出答案。

总复杂度为 O(n\log n)\ \sim\ O(n\log ^2n)

Problem and Tutorial by he_____he

Solution(Hard Version) by Elegia

Easy Version Code:

#include<bits/stdc++.h>

#define pb push_back
#define mp make_pair
#define fi first
#define se second

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;

template <typename T> bool chkmax(T &x,T y){return x<y?x=y,true:false;}
template <typename T> bool chkmin(T &x,T y){return x>y?x=y,true:false;}

int readint(){
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

const int cys=998244353;
int n;
ll fac[5005],inv[5005],d[5005][5005],ans[5005];

ll qpow(ll x,ll p){
    ll ret=1;
    for(;p;p>>=1,x=x*x%cys) if(p&1) ret=ret*x%cys;
    return ret;
}

int main(){
    n=readint();
    d[0][0]=fac[0]=inv[0]=1;
    for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%cys;
    inv[n]=qpow(fac[n],cys-2);
    for(int i=n-1;i>=1;i--) inv[i]=inv[i+1]*(i+1)%cys;
    for(int i=1;i<=n;i++) for(int j=1;j<=i;j++) d[i][j]=(d[i-1][j-1]*(i-j+1)+d[i-1][j]*j)%cys;
    for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) ans[i]=(ans[i]+d[j][i]*inv[j])%cys;
    for(int i=1;i<=n;i++) printf("%lld ",ans[i]*fac[n]%cys);
    printf("\n");
    return 0;
}

Hard Version Code:

#include<bits/stdc++.h>

#define pb push_back
#define mp make_pair
#define fi first
#define se second

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef vector<ll> vi;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;

template <typename T> bool chkmax(T &x,T y){return x<y?x=y,true:false;}
template <typename T> bool chkmin(T &x,T y){return x>y?x=y,true:false;}

int readint(){
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

const int cys=998244353,g=3,invg=(cys+1)/3;
int n;
ll ni[200015],fac[200015],inv[200015],tw[200005];

int mod(int x){return x>=cys?x-cys:x;}

ll qpow(ll x,ll p){
    ll ret=1;
    for(;p;p>>=1,x=x*x%cys) if(p&1) ret=ret*x%cys;
    return ret;
}

vi operator+(vi a,vi b){
    vi ret(max(a.size(),b.size()));
    for(int i=0;i<ret.size();i++) ret[i]=mod((i<a.size()?a[i]:0)+(i<b.size()?b[i]:0));
    return ret;
}

vi operator-(vi a,vi b){
    vi ret(max(a.size(),b.size()));
    for(int i=0;i<ret.size();i++) ret[i]=mod((i<a.size()?a[i]:0)+cys-(i<b.size()?b[i]:0));
    return ret;
}

vi operator*(vi a,ll b){
    for(int i=0;i<a.size();i++) a[i]=1ll*a[i]*b%cys;
    return a;
}

vi operator>>(vi a,int b){
    for(int i=0;i<a.size()-b;i++) a[i]=a[i+b];
    a.resize(a.size()-b);
    return a;
}

namespace poly{
    int N,l;
    int A[1100000],B[1100000],r[1100000],pre1[20][600000],pre2[20][600000];
    void pre(){
        for(int i=1,r=0;i<(1<<19);i<<=1,r++){
            int w=1,wn=qpow(g,(cys-1)/(i<<1));
            for(int j=0;j<i;j++,w=1ll*w*wn%cys) pre1[r][j]=w;
        }
        for(int i=1,r=0;i<(1<<19);i<<=1,r++){
            int w=1,wn=qpow(invg,(cys-1)/(i<<1));
            for(int j=0;j<i;j++,w=1ll*w*wn%cys) pre2[r][j]=w;
        }
    }
    void ntt(int *A,int N,int f){
        for(int i=0;i<N;i++) if(i<r[i]) swap(A[i],A[r[i]]);
        for(int i=1,r=0;i<N;i<<=1,r++){
            for(int j=0;j<N;j+=(i<<1)){
                for(int k=j;k<j+i;k++){
                    int x=A[k],y=1ll*A[k+i]*(f>0?pre1[r][k-j]:pre2[r][k-j])%cys;
                    A[k]=mod(x+y); A[k+i]=mod(x+cys-y);
                }
            }
        }
        if(f<0){
            int invn=qpow(N,cys-2);
            for(int i=0;i<N;i++) A[i]=1ll*A[i]*invn%cys;
        }
    }
    void init(int t){
        N=1,l=0;
        for(;N<t;N<<=1) l++;
        for(int i=0;i<N;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
    }
    void getmul(){
        ntt(A,N,1); ntt(B,N,1);
        for(int i=0;i<N;i++) A[i]=1ll*A[i]*B[i]%cys;
        ntt(A,N,-1);
    }
    vi mul(vi a,vi b){
        init(a.size()+b.size());
        for(int i=0;i<N;i++) A[i]=i<a.size()?a[i]:0;
        for(int i=0;i<N;i++) B[i]=i<b.size()?b[i]:0;
        getmul();
        vi ret(a.size()+b.size()-1);
        for(int i=0;i<ret.size();i++) ret[i]=A[i];
        return ret;
    }
    vi polyinv(vi a,int l){
        if(l==1) return vi(1,qpow(a[0],cys-2));
        a.resize(l);
        vi b=polyinv(a,(l+1)/2);
        init(l<<1);
        for(int i=0;i<N;i++) A[i]=i<l?a[i]:0;
        for(int i=0;i<N;i++) B[i]=i<(l+1)/2?b[i]:0;
        ntt(A,N,1); ntt(B,N,1);
        for(int i=0;i<N;i++) A[i]=1ll*A[i]*B[i]%cys*B[i]%cys;
        ntt(A,N,-1);
        vi t=b*2;
        t.resize(l);
        for(int i=0;i<l;i++) t[i]=mod(t[i]+cys-A[i]);
        return t;
    }
    vi polydiff(vi a){
        for(int i=0;i<a.size()-1;i++) a[i]=1ll*(i+1)*a[i+1]%cys;
        a.resize(a.size()-1);
        return a;
    }
    vi polyint(vi a){
        a.resize(a.size()+1);
        for(int i=a.size()-1;i>=1;i--) a[i]=1ll*a[i-1]*ni[i]%cys;
        a[0]=0;
        return a;
    }
    vi polyln(vi a,int l){
        return polyint(mul(polydiff(a),polyinv(a,l)));
    }
    vi polyexp(vi a,int l){
        if(l==1) return vi(1,1);
        a.resize(l);
        vi b=polyexp(a,(l+1)/2);
        init(l<<1);
        vi t=mul(b,vi(1,1)-polyln(b,l)+a);
        t.resize(l);
        return t;
    }
    vi polypow(vi a,int l,int k){
        return polyexp(polyln(a,l)*k,l);
    }
}

vi getans(){
    vi f(0);
    for(int i=0;i<=n+1;i++) f.push_back(inv[i+1]);
    vi tmp=poly::mul(f,poly::polyinv((vi(1,1)-f)>>1,n+1));
    vi tw(0); tw.resize(n);
    for(int i=0;i<n;i++) tw[i]=tmp[i+1];
    vi h(0);
    h.push_back(1),h.push_back(1);
    h=poly::polyinv(poly::polyln(h,n+3)>>1,n+2);
    vi g=poly::polyinv((vi(1,1)-h)>>1,n+1);
    vi ph=poly::polypow(h,n+1,n+1);
    vi t1=poly::mul(g,ph);
    vi tmp1=poly::mul(poly::polydiff(h),ph);
    tmp1.resize(n+1);
    vi tmp2=poly::mul(g,g);
    tmp2.resize(n+1);
    vi t2=poly::mul(tmp1,tmp2);
    for(int i=0;i<n;i++) tw[i]=mod(tw[i]+cys-ni[n+1]*mod(t1[i+1]*(n-i+1)%cys+t2[i+1])%cys);
    for(int i=0;i<n;i++) tw[i]=tw[i]*fac[i]%cys;
    reverse(tw.begin(),tw.end());
    tmp.clear();
    for(int i=0;i<n;i++) tmp.push_back(i&1?cys-inv[i]:inv[i]);
    tw=poly::mul(tw,tmp);
    tw.resize(n);
    reverse(tw.begin(),tw.end());
    return tw;
}

int main(){
    poly::pre();
    n=readint();
    fac[0]=inv[0]=1;
    for(int i=1;i<=n+5;i++) fac[i]=fac[i-1]*i%cys;
    inv[n+5]=qpow(fac[n+5],cys-2);
    for(int i=n+4;i>=1;i--) inv[i]=inv[i+1]*(i+1)%cys;
    for(int i=1;i<=n+5;i++) ni[i]=inv[i]*fac[i-1]%cys;
    vi res=getans();
    for(int i=0;i<n;i++) printf("%lld ",res[i]*fac[n]%cys*inv[i]%cys);
    printf("\n");
    return 0;
}