「NOIP 2023 模拟赛 20230711 B」过往未来

发布时间 2023-07-11 19:00:47作者: ClapEcho233

summarization

给定一个 \(n\) 个节点的树,定义 \(x_1,x_2,\cdots,x_k\) 生成的子树为树中边数最少的包含 \(x_1,x_2,\cdots,x_k\) 的连通块。

对所有可能的 \(x_1,x_2,\cdots,x_k\quad(1\le x_1<x_2<\cdots<x_k\le n)\),求 \(x_1,x_2,\cdots,x_k\) 生成的子树的大小(边数和)总和。

solution

考虑分别计算每一条边的贡献。

若这条边要产生贡献,当且仅当它的两边都有点被选中。若这条边深度较大的点为根的子树的大小为 \(x\),整棵树的大小为 \(n\)

那么总的选点方案数为 \(C_n^k\),全选在子树中的方案为 \(C_x^k\),全选在字数外的方案为 \(C_{n-x}^k\),则跨越这条边的方案数为 \(C_n^k-C_x^k-C_{n-x}^k\)

code

CI N = 1e6, N2 = 2e6; const ll mod = 998244353; int n, k; ll inv[N + 5], invf[N + 5], sz[N + 5], ans = 0;
namespace graph {
	int nxt[N2 + 5], to[N2 + 5], hd[N + 5], cnt = 0;
	void A (int u, int v) {++ cnt; to[cnt] = v; nxt[cnt] = hd[u]; hd[u] = cnt;}
} using namespace graph;
ll Pow (ll x, ll p) {ll r = 1; for (; p; p >>= 1, x = x * x % mod) if (p & 1) r = r * x % mod; return r;}
void init () {
	RI i, j; inv[0] = inv[1] = 1; for (i = 2; i <= n; ++ i) inv[i] = inv[i - 1] * i % mod; invf[n] = Pow (inv[n], mod - 2);
	for (i = n - 1; i >= 0; -- i) invf[i] = invf[i + 1] * (i + 1) % mod;
}
ll C (int x, int y) {return inv[x] * invf[y] % mod * invf[x - y] % mod;} 
void dfs (int now, int fa) {
	RI i, j; sz[now] = 1; for (i = hd[now]; i; i = nxt[i]) {
		if (to[i] == fa) continue; dfs (to[i], now); sz[now] += sz[to[i]];
	} if (now != 1) ans = (ans - (k > sz[now] ? 0 : C (sz[now], k)) - (k > n - sz[now] ? 0 : C (n - sz[now], k)) + mod + mod) % mod;
}
int main () {
	RI i, j; for (Read (n, k), i = 1; i < n; ++ i) {int x, y; Read (x, y); A (x, y); A (y, x);} init (); ans = C (n, k) * (ll)(n - 1) % mod;
	dfs (1, 0); printf ("%lld\n", ans);
	return 0; 
}