The 2020 ICPC Asia Macau Regional Contest B Boring Problem
比较模板的题。
题目
给定一个字符串 \(S\),\(n\) 个字符串 \(T_1,T_2,\dots,T_n\),每个串长度都是 \(m\),一个长度为 \(k\) 的有理数序列 \(p_1,p_2,\dots,p_k\) 保证 \(\sum p_i=1\)。每个字符串由前 \(k\) 个小写字母构成。
我们进行下面的过程:
- 如果存在 \(j(1\le j\le n)\) 满足 \(T_j\) 是 \(S\) 的子串,那么停止这个过程。
- 否则按 \(p_i\) 的概率将第 \(i\) 个小写字母加到 \(S\) 的末尾,然后继续这个过程。
定义 \(f(S,T,p)\) 表示停止的时候 \(S\) 的期望长度。
给一个字符串 \(R\),对 \(R\) 的每个前缀,求出答案。也就是求出 \(f(R[1\dots i],T,p)\) 对 \(i\in [1,|R|]\) 的所有值。
\(n\le 100,nm\le 10000,|R|\le 10^4\),答案对 \(1\ 000\ 000\ 007 取模\)
题解:
一个显然的做法是建出 \(T_1,T_2,\cdots,T_n\) 的 AC 自动机,则题目相当于在 AC 自动机上随机游走,问走到叶结点之一的期望次数。
设 \(E(x_i)\) 表示从 AC 自动机上第 \(i\) 个点出发,期望多少次能走到一个叶结点,列方程后高斯消元即得答案。未知数个数达到 \(O(nm)\),时间复杂度 \(O(n^3m^3)\),过不了。
考虑优化未知数个数。观察方程 \(E(x_u)=1+\sum_{c=1}^k p_c E(x_{\operatorname{next}_{u,c}})\)。
(\(\operatorname{next}_{u,c}\) 表示从点 \(u\) 走 \(c\) 的转移边到的结点)中,\(\operatorname{next}_{u,c}\) 要么是 Trie 树上 \(u\) 的儿子,要么深度不超过 \(u\) 的深度。
考虑树剖:对每个点 \(u\) 任选一个儿子 \(v\) 作为偏爱儿子进行树链剖分。那这个方程就可以看作把 \(v\) 用 \(u\) 的其他儿子和深度不超过 \(u\) 的深度的结点表示出来。
那么我们发现经过代入,每个结点的答案都可以通过一些链顶结点的答案来表示。怎么做呢?我们按从浅到深的顺序来做。注意到对一个方程 \(E(x_u)=1+\sum_{c=1}^k p_c E(x_{\operatorname{next}_{u,c}})\),它所涉及到的最深层的结点是 \(u\) 的全部儿子,而它们除了偏爱儿子 \(v\) 以外全是链顶结点。而对于所涉及到的深度不超过 \(u\) 的深度的结点,由于我们是按从浅到深的顺序,所以一定已经被表出,代入即可。
由于叶子结点不超过 \(n\) 个,所以剖出链的个数 \(O(n)\),未知数个数 \(O(n)\),总时间复杂度 \(O(n^3+n^2mk+|R|)\),可以通过。
此外这题还可以使用概率生成函数。
不熟悉概率生成函数的同学可以先做做 歌唱王国 和 硬币游戏。
首先把输入的 \(T_1,T_2,\cdots,T_n\) 拿去去重。
先考虑 \(S\) 初始为空的情况怎么做。
记 \(F_i(x)=\sum_{j\ge 0} f_{i,j}x^j,G(x)=\sum_{j\ge 0} g_jx^j\),其中 \(f_{i,j}\) 表示随机第 \(j\) 次时恰好跟 \(T_i\) 匹配上的概率,\(g_j\) 表示随机第 \(j\) 次时还没有结束的概率。
根据概率生成函数的基础知识,我们要求的是 \((\sum F_i)'(1)\)。
由于 \(g_{j-1}-g_{j}=\sum_i f_{i,j}\),于是有 \(1+xG(x)=G(x)+\sum_i F_i(x)\)。
此外我们根据 \(n\) 个串的信息还能列出 \(n\) 个方程。具体来说,若当前还没有结束随机过程,这时我们在串尾接上 \(T_i=T_{i,1}T_{i,2}\cdots T_{i,m}\) 后,随机过程必然是已经结束了。然而也可能还没有接完 \(T_i\) 就已经匹配完了。总之:\(G(x)\prod_{k=1}^m(p_{T_{i,k}}x)=\sum_{j=1}^n F_j(x)\sum_{l|T_i[1:l]=T_j[m-l+1:m]}\prod_{k=l+1}^m(p_{T_{i,k}}x)\)。
我们最终要求 \((\sum F_i)'(1)\)。由第一个式子:
\((\sum F_i)(x)=1+(x-1)G(x)\)
\((\sum F_i)'(x)=G(x)+(x-1)G'(x)\)
\((\sum F_i)'(1)=G(1)\)
所以我们求的其实就是 \(G(1)\)。
由后面的式子,\(G(1)\prod_{k=1}^mp_{T_{i,k}}=\sum_{j=1}^n F_j(1)\sum_{l|T_i[1:l]=T_j[m-l+1:m]}\prod_{k=m-l+1}^mp_{T_{j,k}}\)。
此外还有 \(\sum_{j=1}^n F_j(1)=1\)。
\(n+1\) 个方程 \(n+1\) 个未知数,可以解了。最后代入即得 \(G(1)\)。
接下来是 \(S\) 初始不为空的情况,也就是我们给它钦定了一些前缀。
首先如果钦定的前缀里就包含了某个 \(T_i\),那就不用做了。
否则考虑照样列方程。第一个方程是不变的。
第二个方程如何改变呢?关键在于,刚刚列方程时在串尾接上 \(T_i\) 的操作是在哪里都可以做的,如果使用原来的方程就变成了只有开始随机之后才能接上。实际上还没开始随机的时候也可以往后面加 \(T_i\),而这样对原函数方程的贡献形如 \(\sum_{k}q_kx^k\)(除 \(x\) 以外都是常数),而代入 \(1\) 以后就彻彻底底变成常数了。
因此每次只有常数项有改变,不用每次都高斯消元,只要矩阵求逆后乘向量即可。
时间复杂度 \(O(n^3+n^2m+nmk+|R|)\)。