1 条题解

  • 0
    @ 2025-8-24 23:12:30

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Shui_Dream
    AFO

    搬运于2025-08-24 23:12:30,当前版本为作者最后更新于2025-04-10 19:44:22,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    现有一棵 dfs 序为 1,2,...,n1,2,...,n 的树。对于所有 ii 求选出 ii 个点的好排列的方案数。

    好排列满足以下条件:

    • 若点 u,vu,v 都被选中且 uuvv 的祖先,那么在排列中 uuvv 后面。

    • 若点 uu 被选中,那么 uu 必须出现在序列中前 (ni+1)(n-i+1) 个位置。

    考虑如果没有限制 2,第一个限制是简单的内向树拓扑序计数问题。简单树上背包可以做到 O(n2)O(n^2)。设 gu,ig_{u,i} 表示 uu 子树内选出 ii 个点且 uu 被选中的满足限制的序列个数。

    考虑限制 2,容易发现最终序列中是后缀最大值的位置的限制是更严格的。其他位置只要满足限制 1 即可。考虑从大到小将每个被选中的数插入序列,如果插入时插在了序列末尾,那么其为后缀最大值。

    fi,j,kf_{i,j,k} 表示填完了 i\ge i 的数,填了 jj 个,强制当前序列最后一个数填在位置 kk 的方案数。

    分类讨论:

    • ii 没被选中,继承 fi+1f_{i+1} 的状态即可。
    • ii 被选中,且为后缀最大值,ii 填在当前序列末尾,枚举一个位置 xx,满足 k<xni+1k<x\le n-i+1,从 fi+1f_{i+1} 转移过来。显然满足限制 1。
    • ii 被选中,且不为后缀最大值,枚举子树内包括 ii 共选了 xx 个数,那么这 xx 个数填在 kk 前面即可。有 $f_{i,j+x,k}\leftarrow f_{i+sz_i,j,k}\times g_{i,x}\times \binom{k-j}{x}$。

    复杂度 O(n4)O(n^4)。代码很好写。

    #include<bits/stdc++.h>
    #define psb push_back
    using namespace std;
    typedef long long LL;
    inline int read(){
        char ch=getchar(); while(!isdigit(ch) && ch!='-') ch=getchar();
        int x=0,ff=1; if(ch=='-') ff=-1,ch=getchar();
        while(isdigit(ch)) x=(x<<3) + (x<<1) + (ch^48),ch=getchar();
        return x*ff;
    }
    const int N=85,P=998244353;
    inline void add(int &x,int y) {x+=y; x=x>=P?x-P:x;}
    int n,fz[N][N],fzu[N][N],fz2[N],sz[N],rd[N],C[N][N],f[N][N][N]; vector<int> e[N];
    void dfs1(int u){
        sz[u]=0; fz[u][0]=1; rd[u]=u;
        for(int v:e[u]){
            dfs1(v); rd[u]=rd[v];
            for(int i=0;i<=sz[u]+sz[v];i++) fz2[i]=fz[u][i],fz[u][i]=0;
            for(int i=0;i<=sz[u];i++) for(int j=0;j<=sz[v];j++) add(fz[u][i+j],1ll*fz2[i]*fz[v][j]%P*C[i+j][i]%P);
            sz[u]+=sz[v];
        }
        ++sz[u]; for(int i=sz[u];i>=1;i--) fzu[u][i]=fz[u][i-1],add(fz[u][i],fzu[u][i]);
        
    }
    int main(){
        for(int i=0;i<N;i++){
            C[i][0]=C[i][i]=1;
            for(int j=1;j<i;j++) C[i][j]=(C[i-1][j-1]+C[i-1][j])%P;
        }
        n=read();
        for(int i=1;i<n;i++){
            int u=read(),v=read(); if(u>v) swap(u,v);
            e[u].psb(v);
        }
        dfs1(1); f[n+1][0][0]=1;
        for(int i=n,bf;i>=1;i--){
            if(i>1){
                for(int j=0;j<=n-rd[i];j++) for(int k=j;k<=n;k++) if((bf=f[rd[i]+1][j][k]))
                    for(int l=1;l<=sz[i] && l<=k-j;l++) add(f[i][j+l][k],1ll*C[k-j][l]*fzu[i][l]%P*bf%P);
            }
            for(int j=0;j<=n-i;j++) for(int k=j;k<=n;k++) if((bf=f[i+1][j][k])){
                if(i>1) add(f[i][j][k],bf);
                for(int l=k+1;l<=n;l++) if(l<=n-i+1) add(f[i][j+1][l],bf);
            }
        }
        for(int i=1;i<=n;i++) printf("%d ",f[1][i][i]);
        puts("");
        return 0;
    }
    
    • 1

    信息

    ID
    11934
    时间
    2500ms
    内存
    768MiB
    难度
    7
    标签
    递交数
    0
    已通过
    0
    上传者