题解:P17316 [KismetOI 2026 I] 孤独绽放的彼岸花

考察选手是否做过 CF995F。

题目相当于给每个节点赋一个权值 \(a_u\),满足:

  • \(a_u\in[0,X]\)
  • \(a_{fa_u}\leq a_u\)
  • 存在 \(dep_u=k\) 的点 \(u\),使得 \(a_u\leq S\)

先判掉一些 corner。若树中不存在 \(dep_u=k\) 的点,则答案为 \(0\)。在此基础上,若 \(X\leq S\),则答案为 \(1\)

接下来考虑 \(X>S\) 怎么做。

考虑先求总方案数。这个不就是我们 CF995F 吗?具体来说,令 \(f_{u,i}\) 表示考虑 \(sub_u\) 内的点,给每个节点赋一个 \([0,i]\) 内的权值,满足 \(a_{fa_v}\leq a_v\),此时的方案数。转移时枚举 \(a_u=j\)

\[\begin{align*} f_{u,i}&=\sum_{j=0}^i\prod_{v\in son_u}f_{v,i-j}\\ &=\sum_{j=0}^i\prod_{v\in son_u}f_{v,j} \end{align*} \]

那么答案就是 \(f_{1,X}-f_{1,X-1}\)

构造多项式 \(F_u\) 使得 \(F_u(x)=f_{u,x}\)。对点值做前缀和会使多项式次数 \(+1\),而在叶子节点处有 \(F_u(x)=x+1\),因此容易归纳得出 \(\deg F_u=sz_u\)

于是我们只需维护 \(F_u(0),\cdots,F_u(n-1)\) 这些点值。最后求答案时先对 \(F_1\) 的点值差分一下,再用 \(x\) 坐标连续的单点插值求出答案即可。时间复杂度为 \(\mathcal{O}(n^2)\)

回到本题,容易考虑正难则反,要求对于每个 \(dep_u=k\) 的点,都有 \(a_u>S\)。对于 \(dep_u<k\) 的点 \(u\),定义 \(g_{u,i}\) 表示考虑 \(sub_u\) 内的点,给每个节点赋一个 \([0,X]\) 内的权值,满足:

  • \(a_u=S-i\)
  • \(a_{fa_v}\leq a_v\)
  • 对于每个 \(dep_v=k\) 的点,都有 \(a_v>S\)

此时的方案数。

注意这里 \(i\) 记录的是 \(S-a_u\),这是为了方便维护点值。

转移考虑分讨。若 \(a_v>S\),则 \(sub_v\) 内的 \(dep=k\) 的点自然满足第二个条件,贡献为 \(f_{v,X-S-1}\);若 \(a_v\leq S\),则贡献为 \(\sum_{j=0}^i g_{v,j}\)。由此得到转移:

\[g_{u,i}=\prod_{v\in son_u}\left(f_{v,X-S-1}+[dep_v<k]\sum_{j=0}^ig_{v,j}\right) \]

构造多项式 \(G_u\) 使得 \(G_u(x)=g_{u,x}\)。和前面一样,容易归纳得出 \(\deg G_u<sz_u\),同样维护 \(G_u(0),\cdots,G_u(n-1)\) 即可。计算 \(f_{v,X-S-1}\) 时需要拉插。

最终满足条件的方案数就是 \(G_u(S)\)。设总方案数为 \(C\),那么答案为 \(1-\dfrac{G_u(S)}{C}\)

时间复杂度为 \(\mathcal{O}(n^2)\)

主要代码
int tc, n, k;
ll S, X;
int fa[MAXN], dep[MAXN], sz[MAXN];
vector<int> T[MAXN];
mint f[MAXN][MAXN], g[MAXN][MAXN];
mint fac[MAXN], ifac[MAXN];
mint pre[MAXN], suf[MAXN];

void init(int n) {
	fac[0] = 1;
	for (int i = 1; i <= n; ++i) fac[i] = fac[i - 1] * i;
	ifac[n] = fac[n].inv();
	for (int i = n - 1; i >= 0; --i) ifac[i] = ifac[i + 1] * (i + 1);
}

mint calc(mint *a, mint x, int n) {
	pre[0] = 1;
	for (int i = 1; i <= n; ++i) pre[i] = pre[i - 1] * (x - i + 1);
	suf[n] = 1;
	for (int i = n - 1; i >= 0; --i) suf[i] = suf[i + 1] * (x - i - 1);

	mint res = 0;
	for (int i = 0; i <= n; ++i) res += a[i] * pre[i] * suf[i] * ifac[i] * ifac[n - i] * (n - i & 1 ? -1 : 1);
	return res;
}

void dfs1(int u) {
	sz[u] = 1;
	for (int v : T[u]) {
		dep[v] = dep[u] + 1;
		dfs1(v);
		sz[u] += sz[v];
	}
}

void dfs2(int u) {
	if (T[u].empty()) {
		for (int i = 0; i < n; ++i) {
			f[u][i] = i + 1;
			g[u][i] = 1;
		}
		return;
	}

	fill(f[u], f[u] + n, 1);
	fill(g[u], g[u] + n, 1);
	for (int v : T[u]) {
		dfs2(v);
		for (int i = 0; i < n; ++i) f[u][i] *= f[v][i];
		mint val = calc(f[v], X - S - 1, sz[v]);
		for (int i = 0; i < n; ++i) {
			if (i) g[v][i] += g[v][i - 1];
			g[u][i] *= val + (dep[v] < k ? g[v][i] : 0);
		}
	}
	for (int i = 1; i < n; ++i) f[u][i] += f[u][i - 1];
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	init(MAXN - 5);
	
	cin >> tc;
	while (tc--) {
		cin >> n >> k >> S >> X;
		for (int i = 1; i <= n; ++i) T[i].clear();
		for (int i = 2; i <= n; ++i) {
			cin >> fa[i];
			T[fa[i]].emplace_back(i);
		}

		dfs1(1);
		if (*max_element(dep + 1, dep + n + 1) < k) {
			cout << "0\n";
			continue;
		}

		if (X <= S) {
			cout << "1\n";
			continue;
		}

		dfs2(1);
		for (int i = n - 1; i; --i) f[1][i] -= f[1][i - 1];
		cout << 1 - calc(g[1], S, n - 1) / calc(f[1], X, n - 1) << '\n';
	}
	return 0;
}
posted @ 2026-08-25 19:38  P2441M  阅读(3)  评论(0)    收藏  举报