题目描述:戳这里
题解:
这题如果按照题意做看似非常不可解,但是有一个叫做Min-Max容斥的东西:
M
a
x
(
S
)
=
∑
U
⊂
S
(
−
1
)
∣
U
∣
−
1
M
i
n
(
U
)
Max(S)=\sum_{U\subset S}(-1)^{\left| U \right|-1}Min(U)
Max(S)=U⊂S∑(−1)∣U∣−1Min(U)
对于这题,Max就是答案,也就是
∣
|
∣到
2
n
−
1
2^n-1
2n−1的期望步数。
Min就是对于当前的二进制位的集合,至少
∣
|
∣到一位是1的期望步数。
那么接下来的问题就变成了求Min。
我们列出式子:
M
i
n
(
U
)
=
1
∑
T
∩
U
≠
∅
P
T
Min(U)=\frac{1}{\sum_{T \cap U \ne \emptyset}P_T}
Min(U)=∑T∩U̸=∅PT1
补集转换:
=
1
1
−
∑
T
∩
U
=
∅
P
T
=\frac{1}{1-\sum_{T \cap U = \emptyset}P_T}
=1−∑T∩U=∅PT1
对U再取个补集:
=
1
1
−
∑
(
T
∪
C
R
U
)
=
T
P
T
=\frac{1}{1-\sum_{(T \cup C_RU)= T}P_T}
=1−∑(T∪CRU)=TPT1
那么就是求
C
R
U
C_RU
CRU的子集的P之和。
这可以直接用一个
2
n
⋅
n
2^n \cdot n
2n⋅n的DP就可以预处理出来。
所以总的复杂度就是
O
(
2
n
⋅
n
)
O(2^n \cdot n)
O(2n⋅n)。
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int maxn=25,maxm=(1<<20)+5;
int n;
double ans,f[maxm];
double abs_(double x){if (x<0.0) return -x; return x;}
int main(){
scanf("%d",&n);
int now=0;
for (int i=0;i<(1<<n);i++) {
scanf("%lf",&f[i]);
if (abs_(f[i])>1e-7)
now=now|i;
}
if (now^((1<<n)-1)) {
printf("INF\n");
return 0;
}
for (int i=1;i<=n;i++)
for (int j=0;j<(1<<n);j++)
if (j&(1<<i-1)) f[j]+=f[j^(1<<i-1)];
for (int i=1;i<(1<<n);i++){
int sum=0;
for (int j=1;j<=n;j++) if (i&(1<<j-1)) sum++;
int x=i^((1<<n)-1);
if ((sum-1)%2==0) ans+=1.0/(1.0-f[x]); else ans-=1.0/(1.0-f[x]);
}
printf("%.10lf\n",ans);
return 0;
}