LOJ#6118 鬼牌

upd:是我假了...这题没有爆精...大家要记得这道题是相对误差106...感谢@foreverlasting的指正。

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

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

题意

你有n张牌,每张牌上有一个1m的点数,你每次随机选出两张不同的牌AB,并将A的点数变为B的点数。求将所有牌的点数变成一样的期望步数。用double输出。n109m105

题解

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

假如若干步以后,是这种牌的数量变为0,则这种情况的贡献为0。 否则,若干步以后是这种牌的数量变为n,则这种情况的贡献为

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

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

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

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

怎么求gi呢?首先边界条件有g0=0gn=1,再来考虑一般的情况。

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

gi=12(gi1+gi+1)

于是移项得到:

gi+1=2gigi1

可以发现每一项都依赖前两项,于是我们设g1=x,则可计算出g2=2xg3=3x,那么显然我们可以猜想gi=ix,可以通过归纳法证明其是成立的。

于是得到gn=nx=1,解得x=1n

因此当第一种牌数为i时,最终全变成第一种牌的概率为in

现在来考虑求fi,边界条件有f0=0fn=0,一般情况显然还是可以归纳成fi1fi+1的情况。不过这次还要额外考虑一些事情:

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

j0((j+1)pjq)

整理得其为q(1p)2

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

对于i+1的情况,pq是一样的,因此贡献为q(1p)2i+1n。两者相加为q(1p)22in

可以计算得1p=2i(ni)n(n1)q=i(ni)n(n1)。于是原式可化简为:

n12(ni)

因此可以得到:

fi=12(fi1+fi+1)+n12(ni)

移项可得:

fi+1=2fifi1n1ni

依然设f1=x,则f2=2xn1n1f3=3x2n1n1n1n2,依然可以通过观察和归纳法证明:

fi=ixj=1i1((ij)n1nj)

那么对于fn

fn=nxj=1n1((nj)n1nj)=0

可解得x=(n1)(n1)n

于是继续对fi的式子进行化简:

fi=i(n1)(n1)ni(n1)(j=1i11nj)+(n1)(j=1i1jnj)

我们设Hi为调和数,即Hi=j=1i1j,则第二项等于:

i(n1)(Hn1Hni)

继续推导第三项:

(n1)(j=1i1jnj)(n1)(j=1i1(nnj1))(n1)n(j=1i11nj)(n1)(i1)(n1)n(Hn1Hni)(n1)(i1)

将三项合并,最终得到:

fi=i(n1)(n1)n+(n1)(ni)(Hn1Hni)(n1)(i1)

那么问题来了,n那么大,Hn怎么求呢?标算的做法是将小的调和数预处理,大的用Hxlnx+γ来近似,其中γ为欧拉常数。但是不知道为啥标算的γ取的是0.57...不过实测下来就算预处理前220的调和数以及用较精确的欧拉常数后面的误差也在107左右...再乘以一个大常数算答案精度堪忧。

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

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