题意:
给定 n 张排成一堆的的卡片,每一次从堆顶上等概率随机取出 [1~当前卡片数] 个卡片,如果堆顶的卡片是反面朝上,
则将所有取出的卡片翻转,求问期望取出多少个反面朝上的卡片。
解法:
考虑dp,首先有期望的可加性得
$ans = sum_{i=1}^n{ P(card_i is reversed when got) }$
这样考虑求后面的概率。
用 $f(i)$ 表示以 $i$ 为取出的卡片的顶上的卡片的概率。
$f(1) = 1$
$f(i) = sum{ frac{f(j)}{n-j+1} }$
这样记$h(i,0), h(i,1)$ 分别表示第 $i$ 张卡片被一张正面朝上的卡消去 和 被一张反面朝上的卡消去的概率。
从而有
$$h(i,0) = sum_{1 leq j leq i, S(j-1) = C}{ f(j) frac{n-i+1}{n-j+1} }$$
$h(i,1)$ 递推式同理。
对两个式子记一下前缀和,$O(n)$
#include <iostream> #include <cstdio> #include <cstring> #define LD double #define N 1000010 using namespace std; int n; LD f[N]; char S[N]; int main() { freopen("foreign.in", "r", stdin); freopen("foreign.out", "w", stdout); while(~scanf("%s",S)) { n = strlen(S); f[1] = 1; f[2] = 1.0 / (LD)n; for(int i = 2;i < n;i++) f[i+1] = f[i] + f[i] / (n-i+1); LD sumC = 0, sumW = 0, ans = 0; for(int i = 1;i <= n;i++) { if(S[i-1] == 'C') sumC += f[i] / (n-i+1.0); else sumW += f[i] / (n-i+1.0); if(S[i-1] == 'C') ans += (n-i+1.0) * sumW; else ans += (n-i+1.0) * sumC; } printf("%.10lf ", ans); } fclose(stdin); fclose(stdout); return 0; }