Luogu P5643 [PKUWC2018]随机游走

Luogu P5643 [PKUWC2018]随机游走

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

​ 设 fS,u 为从点 u 出发到达点集 S 内第一个点的期望步数。设 degu 为点 u 的度数,则有

fS,x={1+1degx(fS,fax+vchxfS,v),xS0,xS

直接使用高斯消元是 O(n3) 的,处理 fS,u 的时间复杂度就是 O(2nn3) 的,这显然不行。

​ 考虑优化。假设我们对于给定 S 已经求出了所有的 fS,u,容易发现 fS,u 可以表示为 kufS,fau+bu 的形式。带入上式,则有

fS,x=1+fS,faxdegx+1degxvchx(kvfS,x+bv)(xS)

Ku=vchukv,Bu=vchubv,解上述方程,有

fS,x=fS,faxdegxKx+degx+BxdegxKx

即有 kx=1degxKx,bx=degx+BxdegxKx。于是我们可以直接树形DP求出 kx,bx 的值,进而求出 fS,x 的值。树形DP的时间复杂度是 O(n) 的,这一部分的总时间复杂度就被降到了 O(2nn) 了。

​ 考虑如何处理询问。预处理时对 fS=(1)|S|+1fS,X 做高维前缀和,查询时直接 O(1) 查询即可。总时间复杂度为 O(2nnlogV+q),其中 logV 是处理逆元的复杂度。

参考代码

#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 @   cutx64  阅读(33)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示