BZOJ 4036 [HAOI2015] Set 解题报告
首先我们不能一位一位的考虑,为什么呢?
你想想,你如果一位一位地考虑的话,那么最后就只有 $n$ 个数字,然而他给了你 $2^n$ 个数字,怎么看都不对劲呀。(我是因为这样子弄没过样例才明白的)
所以我们还是要想想其他的方法。
我们是要算步数的期望,然而步数是一个离散的整数,所以我们可以把问题转化一下:
$$E(s) = \sum_{k=1}^{\infty}P(s\ge k)$$
然后就好做了嘛。
我们可以求出一个 $F_i = \sum_{j\subseteq i} p_j$,表示随机选一个数是 $i$ 的子集的概率。
那么就会有:
$$P(s\ge k) = \sum_{i=0}^{2^n-1}(-1)^{c(i)+n+1}\times F_i^{k-1}$$
其中 $c(i)$ 表示 $i$ 的二进制表示中 $1$ 的个数。以上的式子也就是一个容斥的样子,其实说起来就是位运算卷积。然后于是就有:
$$E(s) = \sum_{i=0}^{2^n-1} (-1)^{c(i)+n+1}\sum_{k=0}^{\infty}F_i^{k-1} = \sum_{i=0}^{2^n-1} \frac{(-1)^{c(i)+n+1}}{1 - F_i}$$
然后好像就做完啦。
时间复杂度 $O(n\times2^n)$,空间复杂度 $O(2^n)$。
1 #include <cstdio> 2 typedef long double LD; 3 #define N 1 << 20 4 #define eps 1e-11 5 6 int n, Op[N]; 7 LD A[N]; 8 9 int main() 10 { 11 scanf("%d", &n); 12 Op[0] = n & 1 ? 1 : -1; 13 for (int i = 0; i < (1 << n); i ++) 14 { 15 double x; 16 scanf("%lf", &x); 17 A[i] = x; 18 if (i > 0) Op[i] = -Op[i - (i & -i)]; 19 } 20 for (int k = 1; k < (1 << n); k <<= 1) 21 for (int i = 0; i < (1 << n); i ++) 22 { 23 if (i & k) continue ; 24 A[i + k] += A[i]; 25 } 26 bool ok = 1; 27 for (int i = 0; ok && i < (1 << n) - 1; i ++) 28 if (A[i] + eps > 1) ok = 0; 29 if (!ok) puts("INF"); 30 else 31 { 32 LD ans = 0; 33 for (int i = 0; i < (1 << n) - 1; i ++) 34 ans += Op[i] / (1 - A[i]); 35 printf("%.10lf\n", (double) ans); 36 } 37 38 return 0; 39 }