连号标记(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;
}
::::

浙公网安备 33010602011771号