[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;
}
posted @ 2021-04-29 19:04  zkdxl  阅读(46)  评论(0编辑  收藏  举报