LOJ 3045: 洛谷 P5326: 「ZJOI2019」开关
题目传送门:LOJ #3045。
题意简述
略。
题解
从高斯消元出发好像需要一些集合幂级数的知识,就不从这个角度思考了。
令 \(\displaystyle \dot p = \sum_{i = 1}^{n} p_i\)。
我们考虑一个操作序列 \(\{a_1, a_2, \ldots , a_k\}\),其中 \(1 \le a_j \le n\),就表示第 \(i\) 次按下了开关 \(a_j\)。
那么按 \(k\) 次后恰好得到这个序列的概率就是 \(\displaystyle \prod_{j = 1}^{k} (p_{a_j} / \dot p)\)。
那么我们考虑如果按下这个序列后恰好得到了目标状态 \(s\):
当且仅当对于每个 \(i\)(\(1 \le i \le n\))均满足按下开关 \(i\) 的次数的奇偶性恰好等于 \(s_i\)。
形式化地说,就是对于每个 \(i\) 有 \(\displaystyle \left( \sum_{j = 1}^{k} [a_j = i] \right) \bmod 2 = s_i\)。
那么我们对每个 \(i\) 分开考虑,对于 \(s_i = 0\) 的需要按偶数次,对于 \(s_i = 1\) 的需要按奇数次。
- 对于某个 \(s_i = 0\) 的 \(i\),我们给出这样的数列:\(f_i = \{1, 0, {(p_i / \dot p)}^2, 0, {(p_i / \dot p)}^4, 0, {(p_i / \dot p)}^6, 0, \ldots \}\)。
- 对于某个 \(s_i = 1\) 的 \(i\),我们给出这样的数列:\(f_i = \{0, p_i / \dot p, 0, {(p_i / \dot p)}^3, 0, {(p_i / \dot p)}^5, 0, {(p_i / \dot p)}^7, \ldots \}\)。
可以发现,把所有的 \(i\) 的数列全部二项卷积起来,就得到了一个新的数列 \(f\),这个数列满足:
对于 \(f\) 的第 \(k\) 项 \(f_k\),就表示了当按 \(k\) 下开关时,恰好得到状态 \(s\) 的概率。
因为是 二项卷积,所以我们把这个过程写成 指数型概率生成函数 的形式:
定义 \(\hat F_i (x) = \mathbf{EGF} \left( { \left\{ [j \bmod 2 = s_i] {(p_i / \dot p)}^j \right\} }_{j = 0}^{\infty} \right)\),
也就是每个 \(i\) 对应的上述数列 \(f_i\) 的指数型生成函数,
写做封闭形式,就是 \(\displaystyle \hat F_i (x) = \frac{e^{(p_i / \dot p) x} + {(-1)}^{s_i} e^{-(p_i / \dot p) x}}{2}\)。
所以最终得到的 \(f\) 的 EGF 就是 \(\displaystyle \hat F (x) = \prod_{i = 1}^{n} \frac{e^{(p_i / \dot p) x} + {(-1)}^{s_i} e^{-(p_i / \dot p) x}}{2}\)。
看起来非常的变态,但是还没完!出什么问题了?
首先我们要明确:得到 \(f\) 能干啥?
发现 \(f\) 的性质是:\(f_k\) 表示按恰好 \(k\) 次开关得到状态 \(s\) 的概率,那么根据期望的定义,答案就是 \(\displaystyle \sum_{i = 0}^{\infty} i f_i\)。
这是啥啊,就是 \(f\) 对应的 普通生成函数 \(\displaystyle F(x) = \sum_{i = 0}^{\infty} f_i x^i\),它在 \(1\) 处的导数,也就是 \(F'(1)\)。
(回顾形式幂级数求导,以及求值的定义)
但是 错了,再观察一下,题目要求的是 第一次 到达状态 \(s\) 的期望步数,而不是现在这个样子。
(因为可能不是第一次,而是此前已经经过很多次了。实际上如果直接求这个,甚至是不收敛的)
那么怎么办呢?我们发现需要排除第一次到达 \(s\) 后,又经过若干步返回 \(s\) 的情况,也就是返回原状态了。
由此,我们考虑求出数列 \(g\),其中 \(g_k\) 表示在 \(k\) 步后恰好返回原状态的概率。
那么可以发现,如果令最终答案的数列为 \(h\),有 \(h \ast g = f\)(\(h\) 卷 \(g\) 等于 \(f\),是普通卷积不是二项卷积)。
而 \(g\) 应该如何求得呢?其实就是当全部 \(s_i = 0\) 时的 \(f\) 啦,因为是要返回原状态嘛。
上面说了一堆理论上的东西,现在我们考虑如何实现。
首先发现求的时候是 EGF,但是算答案的时候是 OGF,这很怪。我们观察一下形式看看能不能转换。
对于 \(\hat F\),有形式 \(\displaystyle \hat F (x) = \prod_{i = 1}^{n} \frac{e^{(p_i / \dot p) x} + {(-1)}^{s_i} e^{-(p_i / \dot p) x}}{2}\)。
我们把每个形如 \(a_w e^{(w / \dot p)x}\) 的式子看作一项,可以发现最终 \(w\) 的取值在 \([-\dot p, \dot p]\)。
所以把 \(\hat F (x)\) 表示成 \(\displaystyle \sum_{w = -\dot p}^{\dot p} a_w e^{(w / \dot p) x}\) 的形式后,我们就有 \(\displaystyle f_k = \sum_{w = -\dot p}^{\dot p} a_w {(w / \dot p)}^k\)。
再把这个形式转换成 OGF,得到 \(\displaystyle \mathbf{OGF} (f) = F(x) = \sum_{w = -\dot p}^{\dot p} \frac{a_w}{1 - (w / \dot p) x}\)。
这时候考虑求出每个 \(a_w\),可以发现做一个背包就行了,复杂度为 \(\mathcal O (n \dot p)\)。
(观察背包转移时的系数都是 \(\pm 1 / 2\),可以使用多项式 Exp 优化到 \(\mathcal O (n + \dot p \log \dot p)\),但是没有必要)
对于 \(\displaystyle \mathbf{OGF} (g) = G(x) = \sum_{w = -\dot p}^{\dot p} \frac{b_w}{1 - (w / \dot p) x}\) 同理,我们需要求出每一个 \(b_w\)。
求出所有 \(a_w, b_w\) 之后,我们就掌握了 \(f, g\) 的一些性质,然后对于答案 \(h\),令其普通生成函数为 \(H\)。
则根据上面的解释,有 \(H = F / G\),并且最终我们需要求出 \(H'(1)\)。
因为这里 \(F, G, H\) 都可能有无限项,所以要考虑通过 \(a_w, b_w\) 去求出答案。
考虑除法求导法则:\(\displaystyle H' = {(F / G)}' = \frac{F'G - G'F}{G^2}\)。
所以只要求出 \(F(1), G(1), F'(1), G'(1)\) 即可。
然而很可惜,我们发现因为存在 \(\displaystyle \frac{a_{\dot p}}{1 - (\dot p / \dot p) x}\) 这一项,所以 \(F, G, F', G'\) 在 \(x = 1\) 处不收敛。
我们知道答案一定收敛,所以考虑洛都可以洛做一点变换:把 \(F\) 和 \(G\) 都乘上 \((1 - x)\)。那么就有:
- \(F(1) = a_{\dot p}\)。
- \(\displaystyle F'(1) = \sum_{w = -\dot p}^{\dot p - 1} \frac{a_w}{w / \dot p - 1}\)。
- \(G(1) = b_{\dot p}\)。
- \(\displaystyle G'(1) = \sum_{w = -\dot p}^{\dot p - 1} \frac{b_w}{w / \dot p - 1}\)。
具体计算过程省略,就是按照求导的公式算而已。所以:
\]
求出所有的 \(a_w, b_w\) 后按照此式计算即可,时间复杂度为 \(\mathcal O (n \dot p + \dot p \log mod)\),代码如下:
#include <cstdio>
#include <algorithm>
typedef long long LL;
const int Mod = 998244353, Inv2 = (Mod + 1) / 2;
const int MN = 105, MP = 50005;
inline void Add(int &x, LL y) { x = (x + y) % Mod; }
inline int qPow(int b, int e) {
int a = 1;
for (; e; e >>= 1, b = (LL)b * b % Mod)
if (e & 1) a = (LL)a * b % Mod;
return a;
}
inline int gInv(int b) { return qPow(b, Mod - 2); }
int N, s[MN], p[MN], sump;
int _a[2][MP * 2], _b[2][MP * 2], *a[2] = {_a[0] + MP, _a[1] + MP}, *b[2] = {_b[0] + MP, _b[1] + MP};
int Ans;
int main() {
scanf("%d", &N);
for (int i = 1; i <= N; ++i) scanf("%d", &s[i]), s[i] = s[i] ? -1 : 1;
b[0][0] = a[0][0] = 1;
for (int i = 1; i <= N; ++i) {
scanf("%d", &p[i]);
for (int j = -sump - p[i]; j <= sump + p[i]; ++j) b[1][j] = a[1][j] = 0;
for (int j = -sump; j <= sump; ++j)
Add(a[1][j + p[i]], (LL)Inv2 * a[0][j]),
Add(a[1][j - p[i]], s[i] * (LL)Inv2 * a[0][j]),
Add(b[1][j + p[i]], (LL)Inv2 * b[0][j]),
Add(b[1][j - p[i]], (LL)Inv2 * b[0][j]);
sump += p[i];
std::swap(a[0], a[1]), std::swap(b[0], b[1]);
}
int isump = gInv(sump), *A = a[0], *B = b[0];
for (int j = -sump; j < sump; ++j)
Add(Ans, ((LL)A[j] * B[sump] - (LL)B[j] * A[sump]) % Mod * gInv((LL)j * isump % Mod - 1));
Ans = (LL)Ans * qPow(B[sump], Mod - 3) % Mod;
printf("%d\n", (Ans + Mod) % Mod);
return 0;
}
LOJ 3045: 洛谷 P5326: 「ZJOI2019」开关的更多相关文章
- LOJ 3043: 洛谷 P5280: 「ZJOI2019」线段树
题目传送门:LOJ #3043. 题意简述: 你需要模拟线段树的懒标记过程. 初始时有一棵什么标记都没有的 \(n\) 阶线段树. 每次修改会把当前所有的线段树复制一份,然后对于这些线段树实行一次区间 ...
- LOJ 3089: 洛谷 P5319: 「BJOI2019」奥术神杖
题目传送门:LOJ #3089. 题意简述: 有一个长度为 \(n\) 的母串,其中某些位置已固定,另一些位置可以任意填. 同时给定 \(m\) 个小串,第 \(i\) 个为 \(S_i\),所有位置 ...
- LOJ 3093: 洛谷 P5323: 「BJOI2019」光线
题目传送门:LOJ #3093. 题意简述: 有 \(n\) 面玻璃,第 \(i\) 面的透光率为 \(a\),反射率为 \(b\). 问把这 \(n\) 面玻璃按顺序叠在一起后,\(n\) 层玻璃的 ...
- LOJ 2483: 洛谷 P4655: 「CEOI2017」Building Bridges
题目传送门:LOJ #2483. 题意简述: 有 \(n\) 个数,每个数有高度 \(h_i\) 和价格 \(w_i\) 两个属性. 你可以花费 \(w_i\) 的代价移除第 \(i\) 个数(不能移 ...
- LOJ 2312(洛谷 3733) 「HAOI2017」八纵八横——线段树分治+线性基+bitset
题目:https://loj.ac/problem/2312 https://www.luogu.org/problemnew/show/P3733 原本以为要线段树分治+LCT,查了查发现环上的值直 ...
- LOJ 2249: 洛谷 P2305: 「NOI2014」购票
题目传送门:LOJ #2249. 题意简述: 有一棵以 \(1\) 号节点为根节点的带边权的树. 除了 \(1\) 号节点的所有节点上都有人需要坐车到达 \(1\) 号节点. 除了 \(1\) 号节点 ...
- Loj #3045. 「ZJOI2019」开关
Loj #3045. 「ZJOI2019」开关 题目描述 九条可怜是一个贪玩的女孩子. 这天,她和她的好朋友法海哥哥去玩密室逃脱.在他们面前的是 \(n\) 个开关,开始每个开关都是关闭的状态.要通过 ...
- 洛谷 P4710 「物理」平抛运动
洛谷 P4710 「物理」平抛运动 洛谷传送门 题目描述 小 F 回到班上,面对自己 28 / 110 的物理,感觉非常凉凉.他准备从最基础的力学学起. 如图,一个可以视为质点的小球在点 A(x_0, ...
- 洛谷比赛 「EZEC」 Round 4
洛谷比赛 「EZEC」 Round 4 T1 zrmpaul Loves Array 题目描述 小 Z 有一个下标从 \(1\) 开始并且长度为 \(n\) 的序列,初始时下标为 \(i\) 位置的数 ...
随机推荐
- mysql小白系列_11 MHA
一.MHA是什么?能干什么的 (1)以Perl语言写的一套Mysql故障切换方案,一个脚本管理工具 (2)保障数据库的高可用性 (3)修复多个slave之间的差异日志,最终使所有的slave保持数据一 ...
- PAT-1079 Total Sales of Supply Chain (树的遍历)
1079. Total Sales of Supply A supply chain is a network of retailers(零售商), distributors(经销商), and su ...
- 【学习】Python os模块常用方法 记录
记录一些工作中常用到的用法 os.walk() 模块os中的walk()函数可以遍历文件夹下所有的文件. os.walk(top, topdown=Ture, onerror=None, follow ...
- 001_C语言中运算符的优先级
总的来说就是: 1. 最高:单目运算符(() > * 解引用,&取地址,-取相反数,++等自增(或减)运算,!取反运算...); 2. 次之:双目运算符(算数运算符 > 移位运算符 ...
- [安卓自动化测试] 001.UIAutomator初探
*:first-child { margin-top: 0 !important; } body > *:last-child { margin-bottom: 0 !important; } ...
- Java Spring Cloud 实战之路 - 1 创建项目
0. 前言 该项目使用Maven进行管理和构建,所以需要预先配置好Maven.嗯,在这个系列里就不做过多的介绍了. 1. 创建项目 先创建一个pom.xml 文件,添加以下内容: <?xml v ...
- Redis详解(十一)------ 过期删除策略和内存淘汰策略
在介绍这篇文章之前,我们先来看如下几个问题: ①.如何设置Redis键的过期时间? ②.设置完一个键的过期时间后,到了这个时间,这个键还能获取到么?假如获取不到那这个键还占据着内存吗? ③.如何设置R ...
- GNS3--cisco路由器NAT配置
一.基础 Cisco路由器配置中NAT的主要命令: 静态NAT: 1.指定NAT内部接口 在内网相应接口的接口配置模式下执行:ip nat inside 2.指定NAT外部接口 在外网相应接口的接口配 ...
- WebSocket是什么,有什么作用和特点?
WebSocket是一种在单个TCP连接上进行全双工通信的协议. Websocket是基于HTTP协议的,或者说借用了HTTP的协议来完成一部分握手.具有持久化的特性 特点: 保持连接状态.与HTTP ...
- 深入理解JS:var、let、const的异同
目录 序言 var 与 let 的区别 作用域 重复声明 绑定全局对象 变量提升与暂存死区 let 与 const 异同 参考 1.序言 var.let 和 const 都是 JavaScript 中 ...