【题解】CF1707D-Partial Virtual Trees
给定一格大小为 \(n\) 的树,每次操作可以将树变成一棵它自己的一棵虚树,不能不变。对每个 \(k\in[1,n - 1]\) 求出恰好 \(k\) 次将树变成 \(1\) 号点的方案,\(n\le 2000\)。
对于不能不变的条件,我们简单容斥掉,我们记 \(f_k\) 为可以不变的方案,显然有 \(f_k = \sum\limits_{k = 0}^i\binom{k}{i}ans_i\),可以二项式反演。
那么我们将问题集中于每次变成一棵虚树。由于虚树大小是不增的,我们可以把过程看成删点的过程。那么对于一个点 \(x\) 可以删除,当且仅当它子树中所有点被删了,或者只有一个儿子的子树中还有点没有删。不难验证这就是充分条件。
所以我们可以直接针对这两种情况 DP,设 \(f_{x,i}\) 表示以 \(x\) 为根的子树,在第 \(i\) 次操作后删完,然后我们讨论 \(x\) 结点是否是最后删除的两种情况,如果是,则只用每个儿子的子树满足条件即可。如果不是,那么我们枚举留下的那个儿子,计算贡献。
DP 需要一些技巧,包括前缀和与前缀积优化。时间复杂度 \(\mathcal{O}(N^2)\)。
#define N 2005
int n, m, f[N][N], g[N][N], p[N], q[N], h[N], c[N][N], a[N], ed[N]; vector<int>e[N];
void dfs(int x,int fa){
vector<int>u;
u.pb(0); int s = 0;
go(y, e[x])if(y != fa)
dfs(y, x), u.pb(y), ++s;
f[x][0] = g[x][0] = 1;
memset(h, 0, sizeof(h));
rp(i, m){
p[0] = q[s + 1] = 1; int w = 1;
rp(j, s)p[j] = p[j - 1] * 1LL * g[u[j]][i - 1] % P;
pr(j, s)q[j] = q[j + 1] * 1LL * g[u[j]][i - 1] % P;
rp(j, s)w = w * 1LL * g[u[j]][i] % P;
rp(j, s)
ad(h[j], p[j - 1] * 1LL * q[j + 1] % P),
ad(f[x][i], h[j] * 1LL * f[u[j]][i] % P);
ad(f[x][i], w);
}
rp(i, m)g[x][i] = (g[x][i - 1] + f[x][i]) % P;
}
int main() {
read(n, P), m = n - 1;
rp(i, n - 1){
int x, y; read(x, y);
e[x].pb(y), e[y].pb(x);
}
dfs(1, 0);
rp(t, m){
a[t] = 1;
go(y, e[1])a[t] = a[t] * 1LL * g[y][t - 1] % P;
}
rep(i, 0, n){
c[i][0] = 1;
rp(j, i)c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % P;
}
rp(i, m){
rp(j, i)if(1 & (i - j))su(ed[i], c[i][j] * 1LL * a[j] % P);
else ad(ed[i], c[i][j] * 1LL * a[j] % P);
}
rp(i, m)printf("%d ", ed[i]);
return 0;
}