您的位置:首页 > 其它

BZOJ 4036 [HAOI2015] Set 解题报告

2015-07-13 11:07 375 查看
首先我们不能一位一位的考虑,为什么呢?

你想想,你如果一位一位地考虑的话,那么最后就只有 $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)$。

#include <cstdio>
typedef long double LD;
#define N 1 << 20
#define eps 1e-11

int n, Op
;
LD A
;

int main()
{
scanf("%d", &n);
Op[0] = n & 1 ? 1 : -1;
for (int i = 0; i < (1 << n); i ++)
{
double x;
scanf("%lf", &x);
A[i] = x;
if (i > 0) Op[i] = -Op[i - (i & -i)];
}
for (int k = 1; k < (1 << n); k <<= 1)
for (int i = 0; i < (1 << n); i ++)
{
if (i & k) continue ;
A[i + k] += A[i];
}
bool ok = 1;
for (int i = 0; ok && i < (1 << n) - 1; i ++)
if (A[i] + eps > 1) ok = 0;
if (!ok) puts("INF");
else
{
LD ans = 0;
for (int i = 0; i < (1 << n) - 1; i ++)
ans += Op[i] / (1 - A[i]);
printf("%.10lf\n", (double) ans);
}

return 0;
}


4036_Gromah
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: