[PKUWC 2018] 随机游走

sxk:基础dp

还是套路的 min-max 容斥。

众所周知 min-max 容斥在期望下也成立:

\[E(\max(S))=\sum_{S\subseteq T}(-1)^{|T|+1}E(\min(T)) \]

这样走到点集内所有点的时间的步数相当于到点集内的点最晚时间的步数,可以转化为求最早到点集内的点的期望步数。最后是一个子集和的形式,乘以容斥系数做 FWT 就行了。

我们套路的以题目给定的起点为根,令 \(f_{S,i}\) 表示从第 \(i\) 个点到集合 \(S\) 内的的第一个点的期望步数。它可以走到它的儿子或父亲,设第 \(i\) 个点的度数为 \(d_i\),我们有方程:

\[f_{S,i}={1\over d_i}(f_{S,fa_i}+\sum_{j\in son_i} f_{S,j})+1 \]

然而它和它老父亲的值有关,不好转移。常规思路会想到高斯消元,但是又慢又难写

有另一种方法,显然它们间的关系是一次函数。那么设 \(f_{S,i}=k_{S,i}f_{S,fa_i}+b_{S_i}\)。我们用上边的式子试着把它推成这种形式:

\[f_{S,i}={1\over d_i}(f_{S,fa_i}+\sum_{j\in son_i} f_{S,j})+1 \]

\[f_{S,i}={1\over d_i}(f_{S,fa_i}+\sum_{j\in son_i} (k_{S,j}f_{S,i}+b_{S,j}))+1 \]

\[d_if_{S,i}=f_{S,fa_i}+(\sum_{j\in son_i} k_{S,j})f_{S,i}+\sum_{j\in son_i}b_{S,j}+d_i \]

\[(d_i-\sum_{j\in son_i} k_{S,j})f_{S,i}=f_{S,fa_i}+\sum_{j\in son_i}b_{S,j}+d_i \]

\[f_{S,i}={1\over d_i-\sum_{j\in son_i} k_{S,j}}f_{S,fa_i}+{\sum_{j\in son_i}b_{S,j}+d_i\over d_i-\sum_{j\in son_i} k_{S,j}} \]

所以我们得到:

\[k_{S,i}={1\over d_i-\sum_{j\in son_i} k_{S,j}},b_{S,i}=(\sum_{j\in son_i}b_{S,j}+d_i)\cdot k_{S,i} \]

特别的,若 \(i\) 在集合 \(S\) 中,则 \(k_{S,i}=b_{S,i}=0\)(不用走)。且它的子树不用管 (因为肯定走不到,而且就算算了也对答案没有影响,因为要的只有根节点的值)。

对于根节点(即题目中给定的起点),它没有父亲,答案就是 \(b_{S,rt}\)

有多组询问?提前预处理一下就好了。复杂度 \(\Theta(n2^n)\)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

int en = 0, rt, n, m;

struct edge {
	int head, to, nxt;
} ed[105];

inline void addedge(int from, int to) {
	ed[++en].to = to; ed[en].nxt = ed[from].head; ed[from].head = en;
}

const int mod = 998244353;

inline int power(int a, int b = 998244351) {
	int k = b, y = a, t = 1;
	while (k) {
		if (k & 1) t = (1ll * t * y) % mod;
		y = (1ll * y * y) % mod; k >>= 1;
	} return t;
}

int k[1 << 18 | 1][19], b[1 << 18 | 1][19], d[19], pop[1 << 18 | 1];

inline void dfs(int S, int now, int fa) {
	if ((S >> (now - 1)) & 1) return ;
	int sk = 0, sb = 0;
	for (int i = ed[now].head; i; i = ed[i].nxt) {
		int v = ed[i].to; if (v == fa) continue;
		dfs(S, v, now);
		sk += k[S][v]; if (sk >= mod) sk -= mod;
		sb += b[S][v]; if (sb >= mod) sb -= mod;
	} if (d[now] - sk) k[S][now] = power((d[now] - sk + mod) % mod);
	b[S][now] = (1ll * k[S][now] * ((d[now] + sb) % mod)) % mod;
}

int main() {
	scanf("%d%d%d", &n, &m, &rt); int u, v;
	for (int i = 1; i < n; ++i) {
		scanf("%d%d", &u, &v); ++d[u]; ++d[v];
		addedge(u, v); addedge(v, u);
	} for (int i = 1; i < (1 << n); ++i) dfs(i, rt, 0);
	for (int i = 1; i < (1 << n); ++i) {
		pop[i] = pop[i >> 1] + (i & 1);
		if ((pop[i] ^ 1) & 1) b[i][rt] = mod - b[i][rt];
		if (b[i][rt] == mod) b[i][rt] = 0;
	}
	for (int i = 2, j = 1; i <= (1 << n); i <<= 1, j <<= 1)
		for (int k = 0; k < (1 << n); k += i)
			for (int l = 0; l < j; ++l) {
				b[j + k + l][rt] += b[k + l][rt];
				if (b[j + k + l][rt] >= mod) b[j + k + l][rt] -= mod;
			}
	while (m--) {
		int k, x, tmp = 0; scanf("%d", &k);
		while (k--) { scanf("%d", &x); tmp |= 1 << (x - 1); }
		printf("%d\n", b[tmp][rt]);
	} return 0;
}
posted @ 2021-08-02 14:42  Smallbasic  阅读(29)  评论(0)    收藏  举报