LOJ #2542. 「PKUWC 2018」随机游走(最值反演 + 树上期望dp + FMT)
写在这道题前面 : 网上的一些题解都不讲那个系数是怎么推得真的不良心 TAT
(不是每个人都有那么厉害啊 , 我好菜啊)
而且 LOJ 过的代码千篇一律 ... 那个系数根本看不出来是什么啊 TAT
后来做了 HDU 4035 终于会了.... 感谢 雕哥的帮助 !!!
题意
题解
原本的模型好像我不会那个暴力dp .... 就是直接统计点集中最后经过的点的期望 , 也就是点集中到所有点步数最大值的期望 . (也许可以列方程高斯消元 ? 似乎没分)
但我们考虑转化一下 (因为原来 有道CLJ的题 也是求这个) 把最大值的期望用 最值反演(MinMax容斥) 转化成最小值的期望 就可以算了 ...
最值反演 (又称 MinMax容斥 ) :
\[\displaystyle \max\{S\}=\sum_{T\subseteq S, T \not = \varnothing}(-1)^{|T|-1}\min\{T\}
\]其中 \(S\) 是全集 , \(T\) 是它的一个子集 , 就有这个神奇的定理 ...
证明 ( 来自 DOFY大大的博客 ) :
设最大值为 \(x \in S\) ,那么构造映射 \(f(T) \to x \in T~?~T−x:T+x\) , 也就是有 \(x\) 就去掉 , 没有就加上 。那么当 \(T\) 不为空和 \(\{x\}\) 时,\(T\) 与 \(f(T)\) 因为只相差一个最大值,最小值肯定相同,集合大小只相差 \(1\) ,就抵消了(一一映射),因为没有空集,所以最后只剩下 \(\{x\}\) 的贡献。
然后有了这个 , 每次我们只需要求经过点集中点步数最少的贡献 .
假设我们当前有一个集合 \(S\) , 我们用 \(f(u)\) 表示从 \(u\) 出发 , 第一次访问 \(S\) 中节点的期望步数 .
所以我们有一些显然的式子 :
\(u \in S:\)
\[f(u)=0
\]\(u \not \in S:\)
令 \(d[u]\) 为 \(u\) 在树上的度数(连出来边数) , \(\mathrm{ch}[u]\) 为 \(u\) 的儿子 , \(\mathrm{fa}[u]\) 为 \(u\) 的父亲 .
\[\displaystyle f(u)=[f(\mathrm{fa}[u])+1+\sum (f(\mathrm{ch[u]})+1)] \times \frac{1}{d[u]}
\]\[\displaystyle =\frac{1}{d[u]}f(\mathrm{fa}[u])+\frac{1}{d[u]}\sum f(\mathrm{ch}[u])+1
\]
不难发现 每个点的答案可以只保留它父亲的答案和一个常数的贡献
( 可以理解成全都能倒推回去 , 因为那个就算没有 \(u \in S\) 的限制 , 叶子的贡献也只与父亲有关 )
假设令它为 $$f(u)=A_uf(\mathrm{fa}[u])+B_u$$
以及 \(v = \mathrm{ch}[u]\)
那么有 $$\displaystyle \sum f(\mathrm{ch[u]})=\sum f(v) = \sum(A_v f_u + B_v)$$
把这个回代就有
\]
除过去就可以得到每个递推式的 \(A,B\) 了 qwq
然后随便写写就行啦 , 复杂度 \(O((n+Q) \cdot 2^n)\) ... 其实后面那个复杂度是对于每个询问枚举子集 .
预处理的话 , 复杂度就变成 \(O(n\cdot 2^n + 3^n)\) 啦 ...
本人利用了一下 \(FMT\) 的子集和变换把复杂度优化到 \(O(n \cdot 2^n + q)\) 。比较好写(好背)。
似乎都可以轻松过掉 ? 主要没有卡询问的复杂度。
代码
#include <bits/stdc++.h>
#define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Set(a, v) memset(a, v, sizeof(a))
using namespace std;
inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;}
inline int read() {
int x = 0, fh = 1; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1;
for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
return x * fh;
}
void File() {
#ifdef zjp_shadow
freopen ("2542.in", "r", stdin);
freopen ("2542.out", "w", stdout);
#endif
}
typedef long long ll;
const int Mod = 998244353;
inline ll fpm(ll x, int power) {
ll res = 1; x = (x % Mod + Mod) % Mod;
for (; power; power >>= 1, (x *= x) %= Mod)
if (power & 1) (res *= x) %= Mod;
return res;
}
const int N = 20;
int n, Q, rt, d[N];
vector<int> G[N]; ll A[N], B[N], invd[N];
void Dp(int u, int fa, int S) {
if ((1 << (u - 1)) & S) { A[u] = B[u] = 0; return ; }
ll totA = 0, totB = 0;
for (int v : G[u]) if (v ^ fa)
Dp(v, u, S), totA += A[v], totB += B[v];
totA %= Mod, totB %= Mod;
ll coef = fpm(Mod + 1 - totA * invd[u], Mod - 2);
A[u] = invd[u] * coef % Mod;
B[u] = (1 + totB * invd[u] % Mod) * coef % Mod;
}
ll Minv[1 << 18]; int bit[1 << 18];
int ans[1 << 18];
int main () {
File();
n = read(); Q = read(); rt = read();
For (i, 1, n - 1) {
int u = read(), v = read();
G[u].push_back(v); G[v].push_back(u);
++ d[u]; ++ d[v];
}
For (i, 1, n) invd[i] = fpm(d[i], Mod - 2);
int maxsta = (1 << n) - 1;
For (i, 0, maxsta) {
Dp(rt, 0, i);
Minv[i] = B[rt];
bit[i] = bit[i >> 1] + (i & 1);
ans[i] = ((bit[i] & 1 ? 1 : -1) * Minv[i] + Mod) % Mod;
}
For (j, 0, n - 1) For (i, 0, maxsta)
if (i >> j & 1) (ans[i] += ans[i ^ (1 << j)]) %= Mod;
while (Q --) {
int k = read(), sta = 0;
while (k --) sta |= (1 << (read() - 1));
printf ("%d\n", ans[sta]);
}
return 0;
}
LOJ #2542. 「PKUWC 2018」随机游走(最值反演 + 树上期望dp + FMT)的更多相关文章
- 【LOJ 2542】【PKUWC2018】 随机游走(最值反演 + 树上期望dp)
哇我太菜啦555555 不妨钦定我们需要访问的点集为$S$,在$S$已知的情况下,我们令$f(x) $表示从$x$走到点集$S$中任意一点的期望步数. 若$x∈S$,则显然$f(x)=0$,否则$f[ ...
- loj 2542 随机游走 —— 最值反演+树上期望DP+fmt
题目:https://loj.ac/problem/2542 因为走到所有点的期望就是所有点期望的最大值,所以先最值反演一下,问题变成从根走到一个点集任意一点就停止的期望值: 设 \( f[x] \) ...
- LOJ #2540. 「PKUWC 2018」随机算法(概率dp)
题意 LOJ #2540. 「PKUWC 2018」随机算法 题解 朴素的就是 \(O(n3^n)\) dp 写了一下有 \(50pts\) ... 大概就是每个点有三个状态 , 考虑了但不在独立集中 ...
- 【LOJ2542】【PKUWC 2018】随机游走 min-max容斥 树上高斯消元
题目描述 有一棵 \(n\) 个点的树.你从点 \(x\) 出发,每次等概率随机选择一条与所在点相邻的边走过去. 有 \(q\) 次询问,每次询问给定一个集合 \(S\),求如果从 \(x\) 出发一 ...
- LOJ2542 随机游走 Min-Max容斥+树上期望DP
搞了一下午 真的是啥都不会 首先这道题要用到Min-Max容斥 得到的结论是 设 $Max(S)$表示集合里最晚被访问的节点被访问的期望步数 设 $Min(S)$表示集合里最早被访问的节点被访问的期望 ...
- LOJ #2541. 「PKUWC 2018」猎人杀(容斥 , 期望dp , NTT优化)
题意 LOJ #2541. 「PKUWC 2018」猎人杀 题解 一道及其巧妙的题 , 参考了一下这位大佬的博客 ... 令 \(\displaystyle A = \sum_{i=1}^{n} w_ ...
- LOJ #2538. 「PKUWC 2018」Slay the Spire (期望dp)
Update on 1.5 学了 zhou888 的写法,真是又短又快. 并且空间是 \(O(n)\) 的,速度十分优秀. 题意 LOJ #2538. 「PKUWC 2018」Slay the Spi ...
- loj2542 「PKUWC2018」随机游走 MinMax 容斥+树上高斯消元+状压 DP
题目传送门 https://loj.ac/problem/2542 题解 肯定一眼 MinMax 容斥吧. 然后问题就转化为,给定一个集合 \(S\),问期望情况下多少步可以走到 \(S\) 中的点. ...
- LOJ #2537. 「PKUWC 2018」Minimax (线段树合并 优化dp)
题意 小 \(C\) 有一棵 \(n\) 个结点的有根树,根是 \(1\) 号结点,且每个结点最多有两个子结点. 定义结点 \(x\) 的权值为: 1.若 \(x\) 没有子结点,那么它的权值会在输入 ...
随机推荐
- 两种方法操作其它mac应用的窗口
(图文基本无关) 如果单纯说简单方便,其使用AppleScript更好,特别是现在有了JS的加入,比如: (* This Apple script will resize any program wi ...
- Django中的templates(你的HTML页面放哪里)
本文回答Django里面的HTML文件应该怎么放,以及Django是如何查找模板文件的. 到目前为止我们没有使用HTML页面,在之前的说明中所有内容都是写死在程序里的,如果你想改变内容就要修改代码.但 ...
- Python进阶:切片的误区与高级用法
2018-12-31 更新声明:切片系列文章本是分三篇写成,现已合并成一篇.合并后,修正了一些严重的错误(如自定义序列切片的部分),还对行文结构与章节衔接做了大量改动.原系列的单篇就不删除了,毕竟也是 ...
- DocX开源WORD操作组件的学习系列三
DocX学习系列 DocX开源WORD操作组件的学习系列一 : http://www.cnblogs.com/zhaojiedi1992/p/zhaojiedi_sharp_001_docx1.htm ...
- HDFS架构及原理
原文链接:HDFS架构及原理 引言 进入大数据时代,数据集的大小已经超过一台独立物理计算机的存储能力,我们需要对数据进行分区(partition)并存储到若干台单独的计算机上,也就出现了管理网络中跨多 ...
- Perl IO:简介和常用IO模块
三篇Perl IO基础类文章: Perl的IO操作(1):文件句柄 Perl的IO操作(2):更多文件句柄的模式 Perl文件句柄相关的常见变量 IO对象和IO::Module家族模块 无论是哪种高级 ...
- 学习编写Dockerfile
前言 我们学习docker的话,其主要目的还是要用来改变我们部署应用程序的传统习惯,达到解放生产力,解放人力的目的.这篇则自己来熟悉一下dockerfile常用命令.并且尝试改变下生产环境手动部署应用 ...
- demo_2
业务层 package com.demo.service; import com.demo.pojo.User; public interface IUserService { /** * 用户登录 ...
- 配置多个 git 账号的 ssh密钥
背景 在工作中,我们通常会以 ssh 的方式配置公司的 git 账号,但是平时也会使用 github 管理自己的项目.因此,我们需要为自己的 github 创建一个新的 git 账号,这就需要生成新的 ...
- Java开发笔记(三十五)字符串格式化
前面介绍了字符串变量的四种赋值方式,对于简单的赋值来说完全够用了,即便是两个字符串拼接,也只需通过加号把两个目标串连起来即可.但对于复杂的赋值来说就麻烦了,假设现在需要拼接一个很长的字符串,字符串内部 ...