• 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) 个小写字母构成。
    我们进行下面的过程:

    1. 如果存在 (j(1le jle n)) 满足 (T_j)(S) 的子串,那么停止这个过程。
    2. 否则按 (p_i) 的概率将第 (i) 个小写字母加到 (S) 的末尾,然后继续这个过程。

    定义 (f(S,T,p)) 表示停止的时候 (S) 的期望长度。
    给一个字符串 (R),对 (R) 的每个前缀,求出答案。也就是求出 (f(R[1dots i],T,p))(iin [1,|R|]) 的所有值。
    (nle 100,nmle 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_{jge 0} f_{i,j}x^j,G(x)=sum_{jge 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|))

  • 相关阅读:
    Why Visual Studio Team System Isn't A LoadRunner Killer[转载]
    从键盘判断电脑主人
    LoadRunner中添加weblogic监视器(JMX)
    winrunner事务概念的代码应用(毫秒级)
    Delphi中的线程类
    换工作时如何提高自身的待遇(转贴)
    牛人是怎样用函数实现本地化测试的
    肯德基的见闻
    由 12306.cn 谈谈网站性能技术
    20个优秀的 CSS 网格系统(CSS Grid Systems)推荐
  • 原文地址:https://www.cnblogs.com/Camp-Nou/p/14879482.html
Copyright © 2020-2023  润新知