CF464D World of Darkraft - 2
又一次跌在了概率期望的坑上。
显然所有物品一样,可以算出一个的期望后乘以 \(k\)。
我一开始像的是设 \(f(i)\) 表示选择一个物品 \(i\) 次的期望收益,\(g(i)(j)\) 表示当前已经选择了 \(i\) 次,装备等级为 \(j\) 的收益期望,然后写了一堆式子最后优化到了 \(O(n)\),然后对着样例调了三个小时...结果晚上睡觉的时候突然想到转移的时候的那个收益还要乘上到达该状态的概率,然后就全挂了。
正解:
根据绿豆蛙的归宿,倒着转移概率为1,无需记录概率。设 \(f(i)(j)\) 表示还有 \(i\) 次选择机会,当前此物品的等级为 \(j\),从这个状态到最后的期望收益。于是当前有三种后续转移:没选择到此物品;选择到此物品但是爆出的装备等级 \(\le j\);选择到此物品并且爆出 \(j+1\) 级。于是有:
\[\begin{aligned}
f_{i,j}&\gets-(\frac{k-1}{k})--f_{i-1,j}\\
&\gets-(\frac{1}{k(j+1)})-- f_{i-1,j}+t,t\le j\\
&\gets-(\frac{1}{k(j+1)})--f_{i-1,j+1}+j
\end{aligned}\]
于是就可以 \(n^2\) 转移了。
但是 \(n \le 10^5\),复杂度无法接受。不过得到等级较大的装备的概率不大,期望得到的等级大概是 \(\sqrt {n/k}\) 级别的。想要得到等级为 \(j\) 的装备的期望次数是 \(2k+3k+4k+...+jk = O(j^2k)\)。于是只保留前几百级的期望即可。可能需要滚动数组。
关键代码:
int nw = 1;
for (int i = 1; i <= n; ++i, nw ^= 1) {
for (int j = 1; j <= up; ++j) {
double tmp = f[nw^1][j], ttmp = f[nw^1][j+1];
f[nw][j] = tmp*(k-1.0)/k + 1.0/k * (1.0/(j+1.0)*ttmp + j/(1.0+j) + j/(1.0+j)*tmp + j/2.0);
}
}
printf("%.10lf\n", f[nw^1][1] * k);