题解:P10013 [集训队互测 2023] Tree Topological Order Counting
前置芝士
- 将
k 个数插入到m 个数中,方案数为\binom{m + k}{k} ,相当于从m + k 个数中拿k 个。 - 一棵树的拓扑序个数是
n!\over \prod sz_u 。对于一棵树有n! 种的排列,因为对于一个点u 他必须在它子树内的点前面,所以只有sz_u-1! 种的排列,相当于除以sz_u ,从根往下递归,总共除以\prod sz_u 题解
下文中所指
u 的子树含u 。
发现一个点的拓扑序,只与他的父亲有关,所以考虑从上往下转移。设
DP 转移:
当从
即(其中
化简后:
统计答案:
计算点
化简:
时间复杂度
时间复杂度
注意 ,要判断数组越界,组合数要取模(可能很大)。
code
#include<bits/stdc++.h>
#define N 5010
#define int long long
#define pb push_back
#define md 1000000007
#define Fo(a, b) for(auto a : b)
#define fo(a, b, c) for(int b = a; b <= c; b++)
#define _fo(a, b, c) for(int b = a; b >= c; b--)
using namespace std;
int n, b[N], fac[N], sz[N], prod[N], C[N][N], f[N][N], ans[N];
vector<int>G[N];
int fast(int a, int p){
int s = 1;
while(p){
if(p & 1) s = s * a % md;
a = a * a % md;
p >>= 1;
}
return s;
}
void init(){
fac[0] = 1;
fo(1, i, 5000) fac[i] = fac[i - 1] * i % md;
fo(0, i, n) C[i][0] = 1;
fo(1, i, n) fo(1, j, i) C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % md;
prod[0] = 1, f[1][1] = 1;
}
void dfs(int u){
sz[u] = prod[u] = 1;
Fo(v, G[u]){
dfs(v);
sz[u] += sz[v], prod[u] = prod[u] * prod[v] % md;
}
prod[u] = prod[u] * fast(sz[u], md - 2) % md;
}
void calc(int v, int u){
int tp = fac[sz[u] - sz[v] - 1];
Fo(cur, G[u]){
if(v == cur) continue;
tp = tp * prod[cur] % md;
}
// cout << "u;" << u << ' ' << "v:" << v << ' ' << "tp:" << tp << "\n";
fo(1, i, n){
if(n - sz[v] - i + 1 >= 0) f[v][i] = (f[v][i - 1] + f[u][i - 1] * C[n - sz[v] - i + 1][sz[u] - sz[v] - 1] % md * tp % md) % md;
}
}
void solve(int u){
Fo(v, G[u]){
calc(v, u), solve(v);
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin >> n;
fo(2, i, n){
int fa; cin >> fa;
G[fa].pb(i);
}
// fo(1, i, n){
// Fo(v, G[i]) cout << v << ' ';
// cout << "\n";
// }
fo(1, i, n) cin >> b[i];
init();
//cout << C[4][2] << ' ';
dfs(1), solve(1);
//fo(1, i, n) cout << prod[i] << ' ';
fo(1, i, n) fo(1, j, n)
if(n >= j) ans[i] = (ans[i] + b[j] * f[i][j] % md * C[n - j][sz[i] - 1] % md * fac[sz[i]] % md * prod[i] % md) % md;
fo(1, i, n) cout << ans[i] << ' ';
return 0;
}