Loading

【题解】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;
}
posted @ 2022-07-19 21:20  7KByte  阅读(74)  评论(0编辑  收藏  举报