Luogu P5643 [PKUWC2018]随机游走

Luogu P5643 [PKUWC2018]随机游走

​ 题目要求的是点集 \(S\) 内所有点被经过的期望步数,这个东西直接做比较难;可以考虑使用 期望意义下的min-max容斥 将其转化为到达点集 \(S\) 内第一个点的期望步数。设 \(h_{S}\) 表示从起点 \(X\) 开始到达点集 \(S\) 内第一个点带期望步数,则由期望意义下的min-max容斥知 \(\mathrm{Ans}=\sum\limits_{T\subseteq S}(-1)^{|T|+1}h_T\)

​ 设 \(f_{S,u}\) 为从点 \(u\) 出发到达点集 \(S\) 内第一个点的期望步数。设 \(\deg_u\) 为点 \(u\) 的度数,则有

\[f_{S,x}=\begin{cases}1+\dfrac 1{\deg_x}\left(f_{S,\operatorname{fa}_x}+\sum\limits_{v\in \operatorname{ch}_x}f_{S,v}\right),\quad&x\notin S\\0,&x\in S\end{cases} \]

直接使用高斯消元是 \(\mathcal O(n^3)\) 的,处理 \(f_{S,u}\) 的时间复杂度就是 \(\mathcal O(2^nn^3)\) 的,这显然不行。

​ 考虑优化。假设我们对于给定 \(S\) 已经求出了所有的 \(f_{S,u}\),容易发现 \(f_{S,u}\) 可以表示为 \(k_uf_{S,\operatorname{fa}_u}+b_u\) 的形式。带入上式,则有

\[f_{S,x}=1+\frac{f_{S,\operatorname{fa}_x}}{\deg_x}+\frac 1{\deg_x}\sum_{v\in \operatorname{ch}_x}(k_vf_{S,x}+b_v)\qquad(x\notin S) \]

\(K_u=\sum\limits_{v\in \operatorname{ch}_u}k_v,\,B_u=\sum\limits_{v\in\operatorname{ch}_u}b_v\),解上述方程,有

\[f_{S,x}=\frac{f_{S,\operatorname{fa}_x}}{\deg_x-K_x}+\frac{\deg_x+B_x}{\deg_x-K_x} \]

即有 \(k_x=\dfrac 1{\deg_x-K_x},\,b_x=\dfrac {\deg_x+B_x}{\deg_x-K_x}\)。于是我们可以直接树形DP求出 \(k_x,b_x\) 的值,进而求出 \(f_{S,x}\) 的值。树形DP的时间复杂度是 \(\mathcal O(n)\) 的,这一部分的总时间复杂度就被降到了 \(\mathcal O(2^nn)\) 了。

​ 考虑如何处理询问。预处理时对 \(f'_S=(-1)^{|S|+1}f_{S,X}\) 做高维前缀和,查询时直接 \(\mathcal O(1)\) 查询即可。总时间复杂度为 \(\mathcal O(2^nn\log V+q)\),其中 \(\log V\) 是处理逆元的复杂度。

参考代码

#include <bits/stdc++.h>
using namespace std;
static constexpr int mod = 998244353;
inline int add(int x, int y) { return x += y - mod, x + (x >> 31 & mod); }
inline int sub(int x, int y) { return x -= y, x + (x >> 31 & mod); }
inline int mul(int x, int y) { return (int64_t)x * y % mod; }
inline void add_eq(int &x, int y) { x += y - mod, x += (x >> 31 & mod); }
inline void sub_eq(int &x, int y) { x -= y, x += (x >> 31 & mod); }
inline void mul_eq(int &x, int y) { x = (int64_t)x * y % mod; }
int qpow(int x, int y) { int r = 1; for (; y; y >>= 1, mul_eq(x, x)) if (y & 1) mul_eq(r, x); return r; }
static constexpr int N = 18;
int n, Q, X, en, head[N], deg[N];
struct Edge { int to, nxt; } e[N * 2];
void add_edge(int u, int v) {
  e[++en] = (Edge){v, head[u]}, head[u] = en;
  e[++en] = (Edge){u, head[v]}, head[v] = en;
  ++deg[u], ++deg[v];
} // add_edge
int f[1 << N], K[N], B[N];
void dfs(const int &S, int u, int fa) {
  if (S >> u & 1) return K[u] = B[u] = 0, void();
  int ks = 0, bs = 0;
  for (int i = head[u], v; i; i = e[i].nxt)
    if ((v = e[i].to) != fa)
      dfs(S, v, u), add_eq(ks, K[v]), add_eq(bs, B[v]);
  K[u] = qpow(sub(deg[u], ks), mod - 2);
  B[u] = mul(K[u], add(deg[u], bs));
} // dfs
int main(void) {
  scanf("%d%d%d", &n, &Q, &X), --X;
  for (int i = 1, u, v; i < n; ++i)
    scanf("%d%d", &u, &v), --u, --v, add_edge(u, v);
  for (int s = 1; s < (1 << n); ++s)
    dfs(s, X, -1), f[s] = (__builtin_parity(s) ? B[X] : sub(0, B[X]));
  for (int i = 0; i < n; ++i)
    for (int s = 0; s < (1 << n); ++s)
      if (s >> i & 1) add_eq(f[s], f[s ^ (1 << i)]);
  while (Q--) {
    int m, s = 0, x; scanf("%d", &m);
    while (m--) scanf("%d", &x), --x, s |= (1 << x);
    printf("%d\n", f[s]);
  }
  exit(EXIT_SUCCESS);
} // main
posted @ 2022-03-01 18:20  cutx64  阅读(46)  评论(0)    收藏  举报