连号标记(serial)

连号标记(serial)

题目大意

给定 \(n\) 个点的树,用一个排列标记整棵树,要求 \(id_{fa_u} < id{u}\)。

一个询问,问有多少种方案使得恰好有 \(k\) 个点满足 \(id_{fa_u} = id{u}+1\) 这样的“连标号”。

\(1 \le n \le 5000 , 0 \le k \le n\)

思路

注意到字眼 恰好,考虑用二项式反演转成求至少 \(k\) 个。

直接的,考虑设 \(dp_{u,i}\) 表示 \(u\) 子树内有 \(i\) 个连标号。(\(siz_u\) 表示 \(u\) 的子树大小不包括 \(u\))

转移也就是模板地从 \(dp_{v,j} , dp_{u,i}\) 转移至 \(dp_{u,i+j}\),只要最后令 \(dp_{u,i} \gets dp{u,i} + dp{u,i-1}\) 即可表示 \(u\) 向子树连一个连标号。

那么考虑转移时标号的方案怎么算,\(i\) 个连标号相当于把 \(i\) 个点缩掉了,这类树上拓扑序结论直接 \(\frac{(siz_u - i)!}{\prod{siz_v!}}\),所以直接 \(\frac{(siz_u - i + siz_v - j)!}{(siz_u - i)! (siz_v - j)!}\)。

::::success[AC code]

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ld;
#define freop(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout);
#define endl '\n'
#define inf 0x7f7f7f7f
#define lb(x) ((x)&-(x))
#define pii pair<int,int>
#define fir first
#define sec second
#define mkp(a,b) make_pair(a,b)
const int N=5005;
const ll mod=998244353;
int c,T,n,k,siz[N];
ll fac[N],inv[N],dp[N][N],t[N],ans;
vector<int> g[N];

inline ll C(int n,int m){return fac[n]*inv[m]%mod*inv[n-m]%mod;}
void dfs(int u,int fa){
    dp[u][0]=1,siz[u]=0;
    for(int v:g[u]) if(v^fa){
        dfs(v,u);
        for(int i = 0;i<=siz[u];++i) t[i]=dp[u][i],dp[u][i]=0;
        //树状dp每个子树都必须贡献所以清空
        for(int i = 0;i<=siz[u];++i) if(t[i])
            for(int j = 0;j<=siz[v];++j) if(dp[v][j])
                //统计只有子树贡献的情况下,前面*后面*其他子树和 v 选分别选 i 和 j 个的方案
                dp[u][i+j]=(dp[u][i+j]+t[i]*dp[v][j]%mod*C(siz[u]+siz[v]-i-j,siz[u]-i))%mod;
        siz[u]+=siz[v];
    }
    for(int i = siz[u]-1;~i;--i) dp[u][i+1]=(dp[u][i+1]+dp[u][i])%mod;
	++siz[u];
}

int main(){
    cin.tie(0)->ios::sync_with_stdio(false);
    cout.tie(0);
    fac[0]=1;
    for(int i = 1;i<N;++i) fac[i]=fac[i-1]*i%mod;
    inv[N-1]=996779413;
    for(int i = N-2;~i;--i) inv[i]=inv[i+1]*(i+1)%mod;
    freop("serial");
//↑以上为初始化↑
//------------------------------
    cin >> c >> T;
    while(T--){
        cin >> n >> k;
        for(int i = 1;i<=n;++i){
            vector<int>().swap(g[i]);
            for(int j = 1;j<=n;++j) dp[i][j]=0;
        }
        for(int i = 1,u,v;i<n;++i)
            cin >> u >> v,
            g[u].push_back(v),
            g[v].push_back(u);
        dfs(1,0),ans=0;
        for(int i = k;i<=n;++i)
            ans=(ans+(i-k&1?-1:1)*C(i,k)%mod*dp[1][i]%mod+mod)%mod;
        cout << ans << endl;
    }
    return 0;
}

::::

posted @ 2026-10-22 20:39  elon_giao  阅读(5)  评论(0)    收藏  举报