博客
关于我
LOJ2542. 「PKUWC2018」随机游走【概率期望DP+Min-Max容斥(最值反演)】
阅读量:791 次
发布时间:2023-02-06

本文共 2329 字,大约阅读时间需要 7 分钟。


思路

我们可以把到每个点的期望步数算出来取max?但是直接算显然是不行的

那就可以用Min-Max来容斥一下

\(g_{s}\)是从x到s中任意一个点的最小步数

\(f_{s}\)是从x到s中任意一个点的最大步数

然后就可以的得到

\(f_{s}=\sum_{t\subseteq s}(-1)^{|t|+1}g_t\)

然后考虑g怎么求

\(p_i\)是i点到任意一个子集中的点的最小步数

\(p_u=\frac{1}{du_u}(1+p_{fa_u})+\frac{1}{du_u}\sum_{v\in child_u}(p_v+1)\)

然后我们令\(p_u=a_up_{fa_u}+b_u\)

很显然有\(p_u=\frac{1}{du_u}\sum(a_vf_u+b_v+1)+\frac{1}{du_u}(p_{fa_u})\)

然后移项可以得到\(a_u=\frac{1}{du_u-\sum a_v},b_u=\frac{\sum(b_v+1)+1}{du_u-\sum a_v}\)

然后因为x是根没有父亲,所以\(g_{s}=(bitcnt(s) \& 1)?b_u:-b_u\)

然后就可以用子集前缀和进行累加了

最后直接输出答案就可以了


#include 
using namespace std;const int Mod = 998244353;const int N = 20;int n, m, x;int a[N], b[N], du[N];int f[1 << N];vector
g[N];int main() {#ifdef dream_maker freopen("input.txt", "r", stdin);#endif function
add = [&](int a, int b) { return (a += b) >= Mod ? a - Mod : a; }; function
sub = [&](int a, int b) { return (a -= b) < 0 ? a + Mod : a; }; function
mul = [&](int a, int b) { return (long long) a * b % Mod; }; function
fast_pow = [&](int a, int b) { int res = 1; for (; b; b >>= 1, a = mul(a, a)) if (b & 1) res = mul(res, a); return res; }; function
bitcnt = [&](int a) { int res = 0; for (; a; a >>= 1) if (a & 1) ++res; return res; }; function
dfs = [&](int u, int fa, int s) { if ((s >> (u - 1)) & 1) return; a[u] = du[u], b[u] = (u == x) ? 0 : 1; // x不用向fa走的1 for (auto v : g[u]) { if (v == fa) continue; dfs(v, u, s); a[u] = sub(a[u], a[v]); b[u] = add(b[u], b[v] + 1); } a[u] = fast_pow(a[u], Mod - 2); b[u] = mul(b[u], a[u]); }; scanf("%d %d %d", &n, &m, &x); for (int i = 1; i < n; i++) { int u, v; scanf("%d %d", &u, &v); g[u].push_back(v); g[v].push_back(u); ++du[u], ++du[v]; } int up = (1 << n) - 1; for (int s = 1; s <= up; s++) { for (int i = 1; i <= n; i++) a[i] = b[i] = 0; dfs(x, 0, s); f[s] = (bitcnt(s) & 1) ? b[x] : (Mod - b[x]) % Mod; } f[0] = 0; for (int i = 1; i <= n; i++) { // 这个循环在外面 for (int s = 1; s <= up; s++) { if ((s >> (i - 1)) & 1) { f[s] = add(f[s], f[s ^ (1 << (i - 1))]); } } } while (m--) { int num, cur, s = 0; scanf("%d", &num); while (num--) { scanf("%d", &cur); s |= 1 << (cur - 1); } printf("%d\n", f[s]); } return 0;}

转载于:https://www.cnblogs.com/dream-maker-yk/p/10109021.html

你可能感兴趣的文章
linux服务器的稳定性优势有哪些
查看>>
Linux服务器还有漏洞?建议使用 OpenVAS 日常检查!
查看>>
Linux查看CPU和内存使用情况
查看>>
linux查看文件大小
查看>>
linux查看端口占用情况
查看>>
Linux查看系统中用户信息,Linux下用于查看系统当前登录用户信息的4种方法
查看>>
linux查看远程文件,linux下远程自动查看日志
查看>>
linux根文件系统 /etc/resolv.conf 文件详解
查看>>
Linux根文件系统详解
查看>>
linux每日一练:Enable multithreading to use std::thread: Operation not permitted问题解决
查看>>
linux版本石器时代,石器时代私服架设教程Linux版
查看>>
linux环境下的各种后台执行
查看>>
Linux环境变量的执行顺序
查看>>
Linux用户与用户组的操作
查看>>
linux用户管理:添加用户、设置密码、修改删除
查看>>
Linux的inode的理解
查看>>
Linux的使用总结(二)
查看>>
Linux的压缩包命令
查看>>
Linux的基础命令, django的安装与使用
查看>>
linux的用户管理
查看>>