[PKUWC2018]随机游走

第一次做用待定系数法解决树上期望dp的题。

首先这道题很显然可以 \(min-max\) 容斥一下,转化成到达点集 \(S\) 一个点的期望步数。考虑这个怎么求。

\(f_u\) 表示从 \(u\) 开始走到达 \(S\) 的期望步数。

对于 \(u \in S, f_u = 0\) , 对于 \(u \notin S, f_u = \frac{1}{deg_u}(f_{fa_u} + \sum_{v\in son_u}f_v) + 1\)

然后考虑用待定系数法 , 设 \(f_u = k_uf_{fa_u} + b_u\) , 那么有 \(f_u = \frac{1}{deg_u}(f_{fa_u}+\sum_{v\in son_u}(k_vf_u+b_v))\)

\[\begin{aligned} f_u &= \frac{1}{deg_u}(f_{fa_u}+\sum_{v\in son_u}(k_vf_u+b_v))\\ f_u &= \frac{1}{deg_u}(f_{fa_u}+(\sum_{v\in son_u}k_v)\times f_u + \sum_{v\in son_u}b_v)\\ deg_uf_u &= f_{fa_u}+(\sum_{v\in son_u}k_v)\times f_u + \sum_{v\in son_u}b_v\\ (deg_u-\sum_{v\in son_u}k_v)f_u &= f_{fa_u} + \sum_{v\in son_u}b_v\\ f_u &= \frac{1}{(deg_u-\sum_{v\in son_u}k_v)}f_{fa_u} + \frac{\sum_{v\in son_u}b_v}{(deg_u-\sum_{v\in son_u}k_v)}\\ \end{aligned} \]

于是就有 \(k_u = \frac{1}{(deg_u-\sum_{v\in son_u}k_v)}f_{fa_u}\) , \(b_u = \frac{\sum_{v\in son_u}b_v}{(deg_u-\sum_{v\in son_u}k_v)}\)

因为根节点没有父亲,所以 \(f_{rt} = b_{rt}\) .

然后用 \(FWT\) 来优化一下 \(min-max\) 容斥. 总复杂度 \(O(n2^n)\)

#include <bits/stdc++.h>

const int N = 20;
const int mod = 998244353;

int n, q, rt;
int f[N], g[N], deg[N];
int F[(1 << 18) + 10];
std::vector<int> path[N];

int add(int x, int y) {
	return x + y >= mod ? x + y - mod : x + y;
}

int qpow(int a, int b = mod - 2) {
	int res = 1;
	while (b) {
		if (b & 1) res = 1ll * res * a % mod;
		a = 1ll * a * a % mod;
		b >>= 1;
	}
	return res;
}

void dfs(int u, int _fa, int S) {
	int sumf = 0, sumg = 0;
	if (S >> (u - 1) & 1) return ;
	for (auto v : path[u]) {
		if (v == _fa) continue;
		dfs(v, u, S);
		sumf = add(sumf, f[v]);
		sumg = add(sumg, g[v]);
	}
	f[u] = qpow(add(deg[u], mod - sumf));
	g[u] = 1ll * qpow(add(deg[u], mod - sumf)) * add(sumg, deg[u]) % mod;
}

int main() {
	scanf("%d%d%d", &n, &q, &rt);
	for (int i = 1; i < n; ++i) {
		int x, y;
		scanf("%d%d", &x, &y);
		path[x].emplace_back(y);
		path[y].emplace_back(x);
		++deg[x], ++deg[y];
	}
	for (int s = 0; s < (1 << n); ++s) {
		for (int i = 1; i <= n; ++i) f[i] = g[i] = 0;
		dfs(rt, 0, s);
		F[s] = g[rt];
		if (~__builtin_popcount(s) & 1) F[s] = mod - F[s];
	}
	for (int i = 1; i < (1 << n); i <<= 1) {
		for (int s = 0; s < (1 << n); ++s) {
			if (s & i) F[s] = add(F[s], F[s ^ i]);
		}
	}
	while (q--) {
		int k, s = 0;
		scanf("%d", &k);
		for (int i = 1; i <= k; ++i) {
			int x;
			scanf("%d", &x);
			s |= 1 << (x - 1);
		}
		printf("%d\n", F[s]);
	}
	return 0;
}
posted @ 2023-02-11 11:32  youwike  阅读(36)  评论(0编辑  收藏  举报