发布于 ,更新于 ,文章内容可能已经过时

概率杂题

P4562 [JXOI2018]游戏

给定区间 ,每次选择一个未选择过的数,将这个数以及它的所有倍数标记,每一种方案全部标记所用次数记为 ,求所有可能 的和。

首先想到每次将所有点标记的实质,就是将 没有此区间内非自身的因数的数标记完。称这样的数为关键数,设关键数有 个,则实际上标记次数就是最后一个关键数的位置。

最后一个关键数的位置...不沾头不沾尾,所以换个方向考虑——最后一个关键数的位置可以替换为它后面的非关键数的数量

实际上, 个关键数把整个区间分成了 份。设区间长度为 ,则一个非关键数在最后一段区间上的期望就是 种情况,每种情况都在段区间上,只有一种情况在目标区间上), 个非关键点在最后一段区间上的期望就是 。(注意这里概率到期望的转换,要想想为什么期望成了求解目标)

于是,最后一个点的位置的期望,即 的期望为下式

求所有可能的 𝑡 的值的和,乘一个阶乘就好了。

     (𝑛+1)𝑘𝑘+1𝑛!=𝑘𝑘+1(𝑛+1)!

取模的话,求完阶乘 ×𝑘 再乘个 逆元 就行了。

P4284 [SHOI2014] 概率充电器

给定一棵树,每条边能导电的概率为 𝑝𝑖,每个点自己带电的概率为 𝑞𝑖,求带电点个数的期望。

根据期望的线性性,考虑求每个点有电的期望。因为只有 0,1 两种情况,因此求概率就行了。

考虑单个点有电的情况:

  • 自己有电
  • 儿子有电,和儿子连边导电
  • 父亲有电,和父亲连边导电

𝑓𝑢 为点 𝑢 带电的期望:

  • 第一种情况的概率就是 𝑞𝑢,所以直接 𝑓𝑢 =𝑞𝑢 就行。
  • 第二种情况非常像树形 DP 的样子,容易想到从叶子节点向上推就行。
    • 考虑两个独立事件发生其中一件即可的概率: 𝑃(𝐴 +𝐵) =𝑃(𝐴) +(1 𝑃(𝐴))𝑃(𝐵),即 𝐴 事件的概率 + (𝐵 事件 + 𝐴 事件未发生 ) 的概率。
    • 进行 dfs,回溯的时候统计:
      • 设当前点为 𝑢,儿子为 𝑣,则有 𝑓𝑢 =𝑓𝑢 +(1 𝑓𝑢)𝑝𝑢,𝑣𝑓𝑣
  • 第三种情况实际上也是树形 DP,只不过变成了正推,从 𝑢 计算对 𝑣 的贡献。
    • 假设当前到达 𝑢,且 𝑓𝑢 已经算好了,考虑如何把贡献传给 𝑓𝑣
    • 容易想到 𝑢 能贡献给 𝑣 电流的那部分概率,实际上是 𝑓𝑢 扔掉 𝑣 这棵子树的部分。
    • 换句话说,贡献 = 𝑢 有电的概率 𝑢 不考虑 𝑣 的有电的概率,再乘边的概率之类的常数即可。
    • 𝑢,𝑣 =𝑒𝑢 不考虑 𝑣 的有电的概率为 𝑔𝑢𝑣 只考虑下方来电有电的概率为 𝑙𝑣,有柿子: 𝑓𝑢=𝑔𝑢+𝑙𝑣𝑝𝑒(1𝑔𝑢)𝑓𝑢=𝑔𝑢+𝑙𝑣𝑝𝑒𝑙𝑣𝑝𝑒𝑔𝑢𝑓𝑢𝑙𝑣𝑝𝑒=𝑔𝑢(1𝑙𝑣𝑝𝑒)𝑔𝑢=𝑓𝑢𝑙𝑣𝑝𝑒1𝑙𝑣𝑝𝑒𝑓𝑢𝑔𝑢=𝑓𝑢𝑓𝑢𝑙𝑣𝑝𝑒1𝑙𝑣𝑝𝑒
    • 考虑完 𝑢 有电的贡献,乘一下边的概率和 𝑣 没电的概率就行了 𝑓𝑣=𝑓𝑣+(1𝑓𝑣)𝑝𝑒(𝑓𝑢𝑓𝑢𝑙𝑣𝑝𝑒1𝑙𝑣𝑝𝑒)

柿子推完了,写两个 dfs 算答案即可。

SPOJ 4060 KPGAME

传送门

CF 1316 F

又是一个重量级题目。

首先我们发现他这个数列是假的,因为求权值的时候是“排序”后再求,子序列也不需要连续,所以我们直接把它当一个数的集合做,不需要维护他们在原数列中的顺序。下面所说的下标都是排序后的下标。

好,接下来考虑一个点对对期望的贡献。
假设有点对 𝑎𝑖,𝑎𝑗,𝑖 <𝑗,显然权值的贡献是 𝑎𝑖𝑎𝑗
然后来看概率,由于是相邻的,所以能随便挑选的只有 𝑖 左边,𝑗 右边的点,总共有 (𝑖 1) +(𝑚 𝑗) 个,满足两个点相邻的子序列数量即为 2(𝑖1)+(𝑚𝑗),而子序列的总数有 2𝑛 个。因此,这两个点相邻的概率为:

2(𝑖1)+(𝑚𝑗)2𝑛=12𝑛𝑖+1𝑗𝑛=12𝑗𝑖+1

于是得出点对 𝑎𝑖,𝑎𝑗,𝑖 <𝑗 对期望的贡献:𝑎𝑖𝑎𝑗2𝑗𝑖+1

然后给定一个数列,其权值的期望直接枚举点对计算就是答案:𝑛𝑖=1𝑛𝑗=𝑖+1𝑎𝑖𝑎𝑗2𝑗𝑖+1

但是这玩意带单点修改,直接用这个式子的话就是一个非常优秀的 Θ(𝑞𝑛2) 的做法。

单点修改。考虑分治。

把一个序列分成两段:[1,𝑚],[𝑚 +1,𝑛],那它的贡献可以分成三部分:

  • [1,𝑚] 的贡献
  • [𝑚 +1,𝑛] 的贡献
  • 跨越两个区间的点对的贡献

前两个可以分治解决,问题在于第三个东西。
首先这个东西的式子是很好写的:𝑚𝑖=1𝑛𝑗=𝑚+1𝑎𝑖𝑎𝑗2𝑗𝑖+1
为了分治,我们得把这个式子拆成左右两半。欸,这 1 𝑚𝑚 +1 𝑛 根本就没交集,那就直接拆开就好了!

     𝑚𝑖=1𝑛𝑗=𝑚+1𝑎𝑖𝑎𝑗2𝑗𝑖+1=𝑚𝑖=1𝑛𝑗=𝑚+1𝑎𝑖𝑎𝑗2𝑗𝑚+𝑚𝑖+1=(𝑚𝑖=1𝑎𝑖2𝑚𝑖+1)(𝑛𝑗=𝑚+1𝑎𝑗2𝑗𝑚)

然后我们就可以直接分治解决了。

设一个区间的答案为 𝑓,上面拆成的两个部分一个是 𝑙𝑠 一个是 𝑟𝑠𝑙𝑒𝑛(𝑥,𝑦) =𝑦 𝑥 +1 表示一个区间的长度。

对于一个区间 [𝑙,𝑟],有如下一堆东西:

{{𝑓(𝑖,𝑖)=0𝑙𝑠(𝑖,𝑖)=𝑟𝑠(𝑖,𝑖)=1𝑓(𝑙,𝑟)=𝑓(𝑙,𝑚)+𝑓(𝑚+1,𝑟)+𝑙𝑠(𝑙,𝑚)𝑟𝑠(𝑚+1,𝑟)𝑙𝑠(𝑙,𝑟)=𝑙𝑠(𝑙,𝑚)+𝑟𝑠(𝑚+1,𝑟)2𝑙𝑒𝑛(𝑙,𝑚)𝑟𝑠(𝑙,𝑟)=𝑟𝑠(𝑚+1,𝑟)+𝑟𝑠(𝑙,𝑚)2𝑙𝑒𝑛(𝑚+1,𝑟)

最终答案是 𝑓(1,𝑛)

这样的分治,最终复杂度是 Θ(𝑞𝑛) 的,还是不够。

这个分治实际上很容易用线段树维护,但是修改权值怎么办?答案是使用权值线段树。

我们将询问离线下来,把原数列和询问中的数字全部离散化,然后存到权值线段树里面,这样显然就把排序的部分做完了
然后对于每个线段树节点,维护当前区间里面的有效点个数,以及上面那一堆东西,但是 𝑙𝑒𝑛 就可以替换成刚才提到的点数了。

push_up 就用上面的式子就好。

这样的做法时间复杂度为 Θ(𝑛 +𝑞log𝑛),非常能过。

但是另一个细节的点来了:离散化。由于两个相同的数作为点对也会产生贡献,所以在离散化的时候给他们分配不同的编号。