bzoj 2554 Color 概率与期望

这道题是真的考验得分技巧

如果n<=5,那么直接用随机数模拟

n大一点的时候呢?

我们分开单独考虑每一个颜色

考虑一个颜色时,可以把这个颜色看成白球,其他颜色看成黑球,我们要求的就是全部变成白球的期望步数,此时我们设\(f[i]\)为当前有i个白球,全部变成白球的期望步数

而且我们每次操作不一定会出现黑变白或白变黑的情况,发生这种情况的概率是\(\frac{2i(n-i)}{n(n-1)}\)(假设当前有i个白球),就相当于进行\(\frac{n(n-1)}{2i(n-i)}\)次操作才能发生颜色改变。

我们就能写下\(f[i]=\frac{n(n-1)}{2i(n-i)} + \frac{1}{2} f[i+1]+\frac{1}{2}f[i-1]\)

但这是错的。

因为我们发现有的时候会出现全部变成黑球的情况,所以这是个条件概率。

设目前有i个白球,最后能全变成白球的概率是\(g[i]\),显然\(g[0]=0\),\(g[n]=1\),\(g[i]=\frac{g[i-1]+g[i+1]}{2}\)

显然\(g\)数组就是个等差数列,\(g[i]=\frac{i}{n}\)

按照比例把概率中的1分给\(f[i-1]\)\(f[i+1]\)

我们就能写下\(f[i]=\frac{n(n-1)}{2i(n-i)} + \frac{i-1}{2i}f[i-1] + \frac{i+1}{2i} f[i+1]\)

这就是对的了。

我们可以用高斯消元处理,时间复杂度为\(O(n^3)\)

但这题n<=10000

这时候就要用到线性高斯消元的技巧了

\(f[i]=k[i]f[i+1]+b[i]\)

考虑利用这个式子将上面方程进行消元,将这个式子中带入方程右边的第二项,得到

$f[i]=\frac {n(n-1)}{2i(n-i)}+\frac {i-1}{2i}k[i-1]f[i]+\frac {i-1}{2i}b[i-1]+\frac {i+1}{2i}f[i+1] $

$ (1-\frac {i-1}{2i}k[i-1])f[i]=\frac {i+1}{2i}f[i+1]+\frac {i-1}{2i}b[i-1]+\frac {n(n-1)}{2i(n-i)} $

$ f[i]=\frac {\frac {i+1}{2i}}{1-\frac {i-1}{2i}k[i-1]}f[i+1]+\frac {\frac {i-1}{2i}}{1-\frac {i-1}{2i}k[i-1]}b[i-1]+\frac {\frac {n(n-1)}{2i(n-i)}}{1-\frac {i-1}{2i}k[i-1]} $

\(f[i]=k[i]f[i+1]+b[i]\)相比,可得

$ k[i] = \frac{\frac{i+1}{2i}}{1-\frac{i-1}{2i}k[i-1]} $

$ b[i] = \frac {\frac {i-1}{2i}b[i-1]+\frac {n(n-1)}{2i(n-i)}}{1-\frac {i-1}{2i}k[i-1]} $

然后由于\(f[0]\)没有意义,就无需从此转移,所以得到特殊情况

\(f[1]=\frac {n(n-1)}{2\times 1\cdot (n-1)}+f_2=\frac n2+f[2]\)

又由于\(f[1]=k[1]f[2]+b[1]\),可以得到在\(i=1\)下的特殊情况:

\(k[1]=1\)

\(b[1]=\frac {n}{2}\)

然后求出\(f[i]\)就能求出答案了。

代码极短

#include<iostream>
#include<cstring>
#include<cstdio>
#define DB double
using namespace std;
int n;
DB ans;
const int N=10010;
int tong[30];
DB k[N],b[N],f[N];
char s[N];
int main()
{
	scanf("%s",s+1);n=strlen(s+1);
	for(int i = 1;i <= n;++ i)++ tong[s[i] - 'A' + 1];
	k[1] = 1; b[1] = 0.5 * n;
	for(int i = 2;i <= n - 1;++ i)
	{
		DB tmp1,tmp2;
		tmp1 = 0.5 / i;
		tmp2 = 1.0 / (1 - (i - 1) * tmp1 * k[i - 1]);
		k[i] = (DB)(i + 1) * tmp1 * tmp2;
		b[i] = ((i - 1) * tmp1 * b[i - 1] + n * (n - 1) * tmp1 / (n - i)) * tmp2;
	}
	for(int i = n - 1;i >= 1;-- i)f[i] = f[i+1] * k[i] + b[i];
	for(int i = 1;i <= 26;++ i)ans += (DB)tong[i] / n * f[tong[i]];
	printf("%0.1f",ans);
	return 0;
}
posted @ 2021-07-16 09:56  wljss  阅读(174)  评论(0编辑  收藏  举报