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\) 位置的数 ...
随机推荐
- 检查可执行App类型是否为executable (腾讯上线预审核报错)otool工具使用
https://blog.csdn.net/lovechris00/article/details/81561627 查看IPA文件的路径 1,解压缩 xcode导出的xxx.ipa文件 2,然后在解 ...
- Java——动态创建Class(不写入文件,直接从内存中创建class)
原文:https://blog.csdn.net/zhao_xinhu/article/details/82499062#commentsedit 参考:https://www.cnblogs.com ...
- sqoop-介绍及安装
1.sqoop概述 sqoop是Apache旗下一款hadoop和关系数据库服务器之间传送数据的工具: 核心的功能: 导入,迁入(从关系型数据库-->hdfs hive hbase) 导出,迁出 ...
- ORCLE 列转行
字符串转多列 实际上就是拆分字符串的问题,可以使用 substr.instr.regexp_substr函数方式 字符串转多行 使用union all函数等方式 wm_concat函数 wm_conc ...
- Ant标签详解--基础操作
Ant的一些核心概念: build.xml:构建文件是以XML 文件来描述的,默认构建文件名为build.xml. project:每个构建文件包含一个工程. property:属性,一 ...
- C# 数据操作系列 - 19 FreeSql 入坑介绍
0. 前言 前几天FreeSql的作者向我推荐了FreeSql框架,想让我帮忙写个文章介绍一下.嗯,想不到我也能带个货了.哈哈,开个玩笑-看了下觉得设计的挺有意思的,所以就谢了这篇文章. 简单介绍一下 ...
- 使用 fileupload 组件完成文件的上传应用
1. 使用 fileupload 组件完成文件的上传应用 commons-dbutils-1.3.jarcommons-fileupload-1.2.1.jar 1). 需求: > 在 uplo ...
- unicode、encode、decode
1.encode与decode:unicode经过encode -> utf-8,反过来为decode. 爬虫读取网页内容和pandas读取csv时,会把读取到的文字内容转成unicode,当我 ...
- oeong.xyz
兴趣使然而搭的小破站:oeong.xyz
- PowerPC-Link Command File解析
https://mp.weixin.qq.com/s/CATWma2mv5IPYGtKZLuGDA 以Code Warrior 11生成的flash版本(FLASH.lcf)为例 一. 参考资 ...