No.10 【NOIP2013模拟9.29】TheSwaps
又是一道期望题.
一个套路:\[Ans=\sum a_i*c_i\]
其中,\(a_i\)表示\(k\)轮变换后第\(i\)个位置上的期望值.
其中,\(c_i\)表示\(i\)这个位置被选到的概率,其实就是\[\frac{i*(n-i+1)}{n*(n+1)*\frac{1}{2}}\].
然后我们很容易列出转移,其实就是看第\(k\)轮交换完之后如果一个位置上的数值为\(a_i\),那么再进行一轮交换,它的数值就变为\[a_i*\frac{n-2}{n}+\frac{2}{n*(n-1)}*(sum- a_i)\]
其中,\(\frac{n-2}{n}\)是第\(i\)个数不选到的概率,其实就是\[\frac{n-1}{n}·\frac{n-2}{n-1}=\frac{n-2}{n}\]
然后被选到的概率显然是\(\frac{2}{n*(n-1)}\),意思就是我先选第\(i\)个数(因为它肯定被交换嘛),然后再在其余中选一个,就是\(\frac{1}{n-1}\),然后因为顺序可以互换,所以要乘一个\(2\),所以最后式子就长上面那样了。
之后我们发现,这样做是\(O(nk)\)的.
考虑优化,就要考虑化简.
其实发现,因为始终都是一些数在换来换去,所以我们可以先假设第\(i\)个数上一开始的位置是\(a_i\),并且一开始概率为\(1\).
然后进行一轮交换之后,仍为\(a_i\)的概率,其实就是上面那坨式子.
然后最后,不为\(a_i\)的概率乘上其他数的和再除以一个\(n-1\),就是期望了.
很好理解吧~
注意,要开long long!!!
#include <iostream>
#include <cstdio>
#include <cstring>
#define F(i, a, b) for (int i = a; i <= b; i ++)
using namespace std;
const int N = 1e6 + 10;
char ch[N]; long long k, len;
long double a[N], s[N], sum, ans;
int main() {
freopen("data.in", "r", stdin);
scanf("%s %lld", ch + 1, &k), len = strlen(ch + 1);
F(i, 1, len) a[i] = ch[i] - '0', sum += a[i];
long double T = 1;
F(i, 1, k)
T = T * (len - 2) * 1.0 / len + (1 - T) * 2 / ((len - 1) * len);
F(i, 1, len)
ans = (long double) ans + ((1.0 * T * a[i] + (1.0 - T) * (sum - a[i]) / (len - 1.0)) * (1.0 * i * (len - i + 1)) / (1.0 * len * (len + 1) / 2.0));
printf("%.13Lf", ans);
}