• LOJ#6118 鬼牌


    ( m upd):是我假了...这题没有爆精...大家要记得这道题是相对误差(10^{-6})...感谢@foreverlasting的指正。

    题是好题,可是标算爆精是怎么回事...要写的和标算一毛一样才能过。

    各位要写的话过了前5个点就当过了吧...

    题意

    你有(n)张牌,每张牌上有一个(1sim m)的点数,你每次随机选出两张不同的牌(A)(B),并将(A)的点数变为(B)的点数。求将所有牌的点数变成一样的期望步数。用(double)输出。(nleq 10^9)(mleq 10^5)

    题解

    那首先有个挺显然的想法是枚举最终变成了哪种牌,那么所有牌就变成了是这种牌与不是这种牌两类。我们这么计算这种情况的贡献:

    假如若干步以后,是这种牌的数量变为(0),则这种情况的贡献为(0)
    否则,若干步以后是这种牌的数量变为(n),则这种情况的贡献为(发生的概率*步数)

    那么只要把每种牌的这个答案加起来就好了。

    现在我们要解决的问题是:有两种牌,第一种有(i)张,第二种有(n-i)张,问最终全变为第一种牌的贡献。

    很显然当(n)固定的时候,答案只和(i)有关。于是我们将其记为(f_i)

    在计算(f_i)之前,我们先来计算辅助数组(g)。其中(g_i)表示有两种牌,第一种有(i)张,第二种有(n-i)张,问最终全变为第一种牌的概率

    怎么求(g_i)呢?首先边界条件有(g_0=0)(g_n=1),再来考虑一般的情况。

    我们来考虑一下第一次对牌数有影响的操作。假如(A)是第一种牌,(B)是第二种牌,那么第一种牌数(-1);假如(A)是第二种牌,(B)是第一种牌,那么第一种牌数(+1)。显然这两种情况的方案数是一样的,于是可以得出(i)会等概率变成(i-1)(i+1),于是就可以得到一个简单的式子:

    [g_i=frac{1}{2}(g_{i-1}+g_{i+1}) ]

    于是移项得到:

    [g_{i+1}=2*g_i-g_{i-1} ]

    可以发现每一项都依赖前两项,于是我们设(g_1=x),则可计算出(g_2=2*x)(g_3=3*x)(dots),那么显然我们可以猜想(g_i=i*x),可以通过归纳法证明其是成立的。

    于是得到(g_n=n*x=1),解得(x=frac{1}{n})

    因此当第一种牌数为(i)时,最终全变成第一种牌的概率为(frac{i}{n})

    现在来考虑求(f_i),边界条件有(f_0=0)(f_n=0),一般情况显然还是可以归纳成(f_{i-1})(f_{i+1})的情况。不过这次还要额外考虑一些事情:

    以变成(i-1)为例,首先我们枚举在几步以后变成(i-1)。设没有影响的操作发生概率为(p),使(i)变为(i-1)的操作发生概率为(q)。则可得到期望额外花费的步数为:

    [sum_{jgeq 0}Big((j+1)*p^j*qBig) ]

    整理得其为(frac{q}{(1-p)^2})

    还要注意的是由于规约到(i-1)的情况后成功的概率是(frac{i-1}{n}),而失败的贡献是(0),因此期望步数只有(frac{i-1}{n})是有效的,贡献为(frac{q}{(1-p)^2}*frac{i-1}{n})

    对于(i+1)的情况,(p)(q)是一样的,因此贡献为(frac{q}{(1-p)^2}*frac{i+1}{n})。两者相加为(frac{q}{(1-p)^2}*frac{2*i}{n})

    可以计算得(1-p=frac{2*i*(n-i)}{n*(n-1)})(q=frac{i*(n-i)}{n*(n-1)})。于是原式可化简为:

    [frac{n-1}{2*(n-i)} ]

    因此可以得到:

    [f_i=frac{1}{2}(f_{i-1}+f_{i+1})+frac{n-1}{2*(n-i)} ]

    移项可得:

    [f_{i+1}=2*f_i-f_{i-1}-frac{n-1}{n-i} ]

    依然设(f_1=x),则(f_2=2*x-frac{n-1}{n-1})(f_3=3*x-2*frac{n-1}{n-1}-frac{n-1}{n-2})(dots),依然可以通过观察和归纳法证明:

    [f_i=i*x-sum_{j=1}^{i-1}Big((i-j)*frac{n-1}{n-j}Big) ]

    那么对于(f_n)

    [f_n=n*x-sum_{j=1}^{n-1}Big((n-j)*frac{n-1}{n-j}Big)=0 ]

    可解得(x=frac{(n-1)*(n-1)}{n})

    于是继续对(f_i)的式子进行化简:

    [f_i=i*frac{(n-1)*(n-1)}{n}-i*(n-1)*Big(sum_{j=1}^{i-1}frac{1}{n-j}Big)+(n-1)*Big(sum_{j=1}^{i-1}frac{j}{n-j}Big) ]

    我们设(H_i)为调和数,即(H_i=sum_{j=1}^{i}frac{1}{j}),则第二项等于:

    [-i*(n-1)*(H_{n-1}-H_{n-i}) ]

    继续推导第三项:

    [(n-1)*Big(sum_{j=1}^{i-1}frac{j}{n-j}Big) ]

    [(n-1)*Big(sum_{j=1}^{i-1}ig(frac{n}{n-j}-1ig)Big) ]

    [(n-1)*n*Big(sum_{j=1}^{i-1}frac{1}{n-j}Big)-(n-1)*(i-1) ]

    [(n-1)*n*(H_{n-1}-H_{n-i})-(n-1)*(i-1) ]

    将三项合并,最终得到:

    [f_i=i*frac{(n-1)*(n-1)}{n}+(n-1)*(n-i)*(H_{n-1}-H_{n-i})-(n-1)*(i-1) ]

    那么问题来了,(n)那么大,(H_n)怎么求呢?标算的做法是将小的调和数预处理,大的用(H_xsimeq ln x+gamma)来近似,其中(gamma)为欧拉常数。但是不知道为啥标算的(gamma)取的是(0.57)...不过实测下来就算预处理前(2^{20})的调和数以及用较精确的欧拉常数后面的误差也在(10^{-7})左右...再乘以一个大常数算答案精度堪忧。

    这里再提供一个精度靠谱复杂度也有保证的做法。注意到答案中计算贡献的调和数其实类似于倒数的后缀和,因此(i)较小的时候显然可以预处理。可以设一个阈值(T),预处理前(T)个后缀和,剩下的数不会超过(frac{n}{T})个,可以用分段打表暴力处理。由于查询量不是很大,打表的间隔可以取大一点减少代码长度。

    还有由于答案有点大最好开(long double)或者自己写精度更高的类型...不过反正标算都爆精了这些好像也是后话了。这么好的题为什么不取模呢,用第二种做法就很靠谱了。

  • 相关阅读:
    目标检测应用化之web页面(YOLO、SSD等)
    传统候选区域提取方法
    非极大值抑制(Non-Maximum Suppression,NMS)
    Darknet windows移植(YOLO v2)
    线性判别分析 LDA
    SVM 支持向量机
    特征-相似度衡量
    布隆过滤器 Bloom Filter
    聚类算法
    图论--最大流
  • 原文地址:https://www.cnblogs.com/Mr-Spade/p/10166891.html
Copyright © 2020-2023  润新知