题目描述
刚开始你有一个数字 (0) ,每一秒钟你会随机选择一个 ([0,2^n-1]) 的数字,与你手上的数字进行或(C++, C 的 |, Pascal 的 or)操作。选择数字 (i) 的概率是 (p[i])(保证 (0le p[i]le 1, sum p[i]=1) ) 问期望多少秒后,你手上的数字变成 (2^n-1) 。
题解
min-max容斥:
(max(S)=sumlimits_{T subset S cap T eq varnothing} (-1)^{|T|+1}min(T))
(max(S)) 表示集合 (S) 的最大元素
证明大概就是除了 (max(S)) 剩一个,其他都加加减减消掉了。。。
如果我们设 (E(x)) 表示二进制中 (x) 这一位第一次变成 (1) 的期望步数,则有
(maxlimits_{xin S}E(x)=sumlimits_{T subset S cap T eq varnothing} (-1)^{|T|+1}minlimits_{yin T}E(y))
(maxlimits_{xin S}E(x)) 也就是 (S) 中所有位都变成 (1) 的期望步数, (minlimits_{xin S}E(x)) 即为 (S) 中至少有 (1) 位变成 (1) 的期望步数
考虑后者怎么求,一步就使得 (S) 中至少有 (1) 位变成 (1) 的概率为 ((1-sumlimits_{icap S=varnothing} p[i])) ,所以期望步数就是 (dfrac{1}{1-sumlimits_{icap S=varnothing} p[i]})
假设 (S_0) 为 (S) 的补集,那么 (sumlimits_{icap S=varnothing} p[i]=sumlimits_{isubseteq S_0} p[i]) ,右式可以用FMT或者状压DP在 (O(n2^n)) 的复杂度中求得 那么 (minlimits_{xin S}E(x)=dfrac{1}{1-sumlimits_{isubseteq S_0} p[i]}) 然后套用上面的minmax容斥即可 答案即为 (maxlimits_{xin S}E(2^n-1))
无解判断方法很多也很好判断就不说了
#include <bits/stdc++.h>
#define N 1100005
using namespace std;
typedef long long ll;
int n;
double a[N], ans;
void FWT(double *F) {
for (int i = 1; i < (1<<n); i<<=1) {
for (int p = i<<1, j = 0; j < (1<<n); j += p) {
for (int k = 0; k < i; k++) {
double x = F[j+k], y = F[j+k+i];
F[j+k] = x; F[j+k+i] = x + y;
}
}
}
}
int main() {
scanf("%d", &n);
for (int i = 0; i < (1<<n); i++) scanf("%lf", &a[i]);
FWT(a);
for (int i = 1; i < (1<<n); i++) {
if (1.0-a[((1<<n)-1)^i] <= 1e-8) {
puts("INF"); return 0;
}
if (__builtin_popcount(i) & 1) ans += 1.0 / (1.0-a[((1<<n)-1)^i]);
else ans -= 1.0 / (1.0-a[((1<<n)-1)^i]);
}
printf("%.10lf", ans);
return 0;
}