suxxsfe

一言(ヒトコト)

P3175 [HAOI2015]按位或(max-min 容斥)

max-min 容斥:
\(\max(S)\)\(S\) 中的最大元素,\(\min(S)\)\(S\) 中的最小元素,则有:

\[\max(S)=\sum_{T \subseteq S}(-1)^{|T|-1} \min(T) \]

\[\min(S)=\sum_{T \subseteq S}(-1)^{|T|-1} \max(T) \]

考虑证明,对于 \(S\) 中第 \(k\) 大的数 \(x_k\) 建立映射 \(f(x_k)=\{1,2\cdots k\}\)
于是对于两数 \(a,b\),有 \(f(\max(a,b))=f(a)\cup f(b),f(\min(a,b))=f(a)\cap f(b)\)
那么以第一个式子为例

\[|f(\max(S))|=|\bigcup_{x_k\in S} f(x_k)|=\sum_{T\subseteq S}(-1)^{|T|-1} |\bigcap_{x_k\in T} f(x_k)|=\sum_{T\subseteq S}(-1)^{|T|-1} |f(\min(T))| \]

由于这显然是个一一映射,所以 \(|f(\min(T))|\) 就可以映射回 \(\min(T)\),于是就证明了这个式子
这个式子在期望下也成立:

\[E(\max(S))=\sum_{T \subseteq S}(-1)^{|T|-1} E(\min(T)) \]

它的变形还可以在第 \(k\) 大,以及第 \(k\) 大的期望下成立:

\[\operatorname{kthmax}(S)=\sum_{T\subseteq S} (-1)^{|T|-k} \tbinom{|T|-1}{k-1} \min(T) \]

\[E(\operatorname{kthmax}(S))=\sum_{T\subseteq S} (-1)^{|T|-k} \tbinom{|T|-1}{k-1} E(\min(T)) \]

当然以上对于 \(\min\) 的情况也是同理
就不证明了


此题 就用到了在期望下的式子
\(S\) 表示 \(2^n-1\) 每一个二进制位的集合,\(\max(S)\) 表示其中出现最晚的位置的出现时间,\(\min(S)\) 则是出现最早的位置的出现时间
要求的显然是 \(E(\max(X))\)\(X\)\(n\) 个二进制位的全集
那么由 \(E(\max(S))=\sum_{T \subseteq S}(-1)^{|T|-1} E(\min(T))\),问题就变成了如何求 \(E(\min(T))\)

对于任意集合 \(U,U\cap T\neq \varnothing\),只要 \(U\) 被加入,\(T\) 就有了“最早出现的位置”,于是每次取数能出现这样的位置的概率就是所有满足要求的 \(U\) 的概率和,那么期望就是再取倒数:

\[E(\min(T))=\frac{1}{\sum_{U\cap T\neq \varnothing} P(U)} \]

但是这样的 \(U\) 不太好求,考虑取 \(T\) 的补集 \(G\),那么 \(U\) 显然就是 \(G\) 的子集,于是用 FWT 的方法求出每个子集和就行了

#include<cstdio>
#include<cmath>
#define reg register
#define LL_INF (long long)(0x3f3f3f3f3f3f3f3f)
#define INT_INF (int)(0x3f3f3f3f)
inline int read(){
	register int x=0;register int y=1;
	register char c=std::getchar();
	while(c<'0'||c>'9'){if(c=='-') y=0;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+(c^48);c=getchar();}
	return y?x:-x;
}
#define N 1048588
int n;
double p[N];
int cnt[N];
inline void FWT(){
	for(reg int o=2,k=1;o<=n;o<<=1,k<<=1){
		for(reg int i=0;i<n;i+=o)for(reg int j=0;j<k;j++) p[i+j+k]+=p[i+j];
	}
}
int main(){
	n=(1<<read());
	for(reg int i=0;i<n;i++) scanf("%lf",p+i),cnt[i]=cnt[i>>1]+(i&1);
	FWT();
	double ans=0;
	for(reg int i=1;i<n;i++) ans+=((cnt[i]&1)?1:-1)/(1-p[i^(n-1)]);
	if(ans>1e20) puts("INF");
	else printf("%lf\n",ans);
	return 0;
}
posted @ 2021-03-11 20:14  suxxsfe  阅读(67)  评论(0编辑  收藏  举报