[HAOI2015] 按位或
\(\text{Problem}:\)[HAOI2015]按位或
\(\text{Solution}:\)
设二进制上第 \(i\) 位变为 \(1\) 的期望时间为 \(t_{i}\),\(\max(S)\) 表示集合 \(S\) 中最大的 \(t_{i}\),\(\min(S)\) 表示集合 \(S\) 中最小的 \(t_{i}\)。由 \(\min-\max\) 容斥,要求的为:
\[\max(S)=\sum\limits_{T\subseteq S}(-1)^{\lvert T\rvert-1}\min(T)
\]
现在问题转化为对每个集合 \(T\) 求出 \(\text{min}(T)\)。考虑将期望展开,有:
\[\begin{aligned}
\min(T)&=\sum\limits_{i=1}^{\infty}iP(\min(T)=i)\\
&=\left(1-\sum\limits_{T\cap S=\varnothing}p_{T}\right)\sum\limits_{i=1}^{\infty}i\left(\sum\limits_{T\cap S=\varnothing}p_{T}\right)^{i-1}\\
&=p\sum\limits_{i=1}^{\infty}i(1-p)^{i-1}
\end{aligned}
\]
这个东西就很经典了,在这里我们重新证一遍:
\[\begin{aligned}
\frac{\min(T)}{p}&=\sum\limits_{i=1}^{\infty}i(1-p)^{i-1}\\
(1-p)\frac{\min(T)}{p}&=\sum\limits_{i=1}^{\infty}i(1-p)^{i}\\
\min(T)&=\sum\limits_{i=0}^{\infty}(1-p)^{i}\\
&=\frac{1}{p}
\end{aligned}
\]
现在要对于每个集合 \(S\),算出 \(S\) 的补集的子集权值和,可以利用子集卷积在 \(O(2^{n}n)\) 的时间复杂度内实现。
\(\text{Code}:\)
#include <bits/stdc++.h>
#pragma GCC optimize(3)
//#define int long long
#define ri register
#define mk make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define is insert
#define es erase
#define vi vector<int>
#define vpi vector<pair<int,int>>
using namespace std; const int N=(1<<20)+5; const double eps=1e-9;
inline int read()
{
int s=0, w=1; ri char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch^48), ch=getchar();
return s*w;
}
int n,m,cnt[N];
double a[N],ans;
inline void OR(double *s)
{
for(ri int i=2;i<=m;i<<=1)
for(ri int j=0,mid=(i>>1);j<m;j+=i)
for(ri int k=0;k<mid;k++)
a[j+mid+k]+=a[j+k];
}
signed main()
{
n=read(), m=(1<<n);
for(ri int i=0;i<m;i++) scanf("%lf",&a[i]), cnt[i]=cnt[i>>1]+(i&1);
OR(a);
for(ri int i=1;i<m;i++)
{
if(1.0-a[(m-1)^i]<eps) return puts("INF")&0;
if((cnt[i]-1)&1) ans-=1.0/(1.0-a[(m-1)^i]);
else ans+=1.0/(1.0-a[(m-1)^i]);
}
printf("%.10lf\n",ans);
return 0;
}
夜畔流离回,暗叹永无殿。
独隐万花翠,空寂亦难迁。
千秋孰能为,明灭常久见。
但得心未碎,踏遍九重天。