loj2542. 「PKUWC2018」随机游走

题意

略。

题解

看到这个(n leq 18),就知道可能是容斥。
结果是min-max容斥。
原来要求一个点出发到一个点集最晚访问的点的期望时间,现在求的则是一个点出发到一个点集最早访问的点的期望时间。
这个东西可以树上dp,而且显然dp出来的东西是可以高斯消元的。
但是这样复杂度不对。
考虑有个树上高消的方法,建立在线性关系的基础上。
可以推一发式子:

[f_i = frac{1}{d_i} (f_{{fa}_i} + sum_{j in {son}_i} f_j) + 1 ]

并设

[f_i = a_i f_{{fa}_i} + b_i ]

(就不推了,懒)

[f_i = frac{1}{d_i - sum_{j in {son}_i} a_j} f_{{fa}_i} + frac{sum_{j in {son}_i} b_j + d_i}{d_i - sum_{j in {son}_i} a_j} ]

然后就可以从下到上回溯的时候(mathcal O(n log p))计算了。
对于多组数据,其实可以直接预处理每种终点集合的答案,具体就是,终点集合里的每个点(i)(a_i, b_i, f_i)都先为0,然后以出发点为根dp,同时计算(a)(b)数组,则到这个子集的答案就是(f_{root})(即(b_{root})), 复杂度(mathcal O(2 ^ n n))
然后询问的时候使用min-max容斥,枚举子集,每次(mathcal O(1))计算即可。
总复杂度(mathcal O(2 ^ n n + nq))

#include <bits/stdc++.h>
using namespace std;

const int N = 19, mod = 998244353;
int n, q, rt, ans;
int a[N], b[N], d[N], fa[N];
int in[N], sgn[1 << N], mn[1 << N];
vector <int> g[N];
int power (int a, int b) {
	int ret = 1;
	for ( ; b; b >>= 1, a = 1ll * a * a % mod) {
		if (b & 1) {
			ret = 1ll * ret * a % mod;
		}
	}
	return ret;
}
int inv (int x) {
	return power(x, mod - 2);
}
void add (int x, int y) {
	g[x].push_back(y), ++d[x];
}
void dfs (int x, int p) {
	if (in[x]) {
		a[x] = b[x] = 0;
		return;
	}
	fa[x] = p, a[x] = b[x] = d[x];
	for (auto y : g[x]) {
		if (y != p) {
			dfs(y, x);
			a[x] = (a[x] - a[y] + mod) % mod;
			b[x] = (b[x] + b[y]) % mod;
		}
	}
	a[x] = inv(a[x]), b[x] = 1ll * b[x] * a[x] % mod;
}
int main () {
	scanf("%d%d%d", &n, &q, &rt);
	for (int i = 1, x, y; i < n; ++i) {
		scanf("%d%d", &x, &y);
		add(x, y), add(y, x);
	}
	for (int s = 1; s < (1 << n); ++s) {
		sgn[s] = -1;
		for (int i = 0; i < n; ++i) {
			if (s >> i & 1) {
				in[i + 1] = 1;
				sgn[s] = -sgn[s];
			} else {
				in[i + 1] = 0;
			}
		}
		dfs(rt, 0);
		mn[s] = b[rt];
	}
	for (int k, x, s; q; --q) {
		scanf("%d", &k), s = 0;
		for (int i = 1; i <= k; ++i) {
			scanf("%d", &x);
			s |= 1 << (x - 1);
		}
		ans = 0;
		for (int t = s; t; t = (t - 1) & s) {
			ans = (ans + mn[t] * sgn[t]) % mod;
		}
		ans = (ans % mod + mod) % mod;
		printf("%d
", ans);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/psimonw/p/11442485.html